界面自动化测试录制工具,让python selenium自动化测试脚本开发更加方便
自动化测试中,QTP和selenium IDE都支持浏览器录制与回放功能,简单的来说就像一个记录操作步骤的机器人,可以按照记录的步骤重新执行一遍,这就是脚本录制。
个人觉得传统录制工具有些弊端,加上要定制支持我自己的自动化框架(python单机版自动化测试框架源代码),所以自己用javascript写了一个录制工具,在控制台打印记录的python脚本如下:

代码如下(初稿,还在不断调试完善中):
var click_textContent = '
var father_level = 0;
var child_ctl = "";
var child_ctl_tmp = "";
var next_focusedElement = null;
window.clickedElement = null; document.addEventListener("click", function(event) {
window.clickedElement = event.target;
console.log(window.clickedElement);
father_level = 0;
myDispose_click(window.clickedElement);
}); function myDispose_click(focusedElement) {
console.log(`开始 父${father_level} -------------------------`);
let tag_name = focusedElement.tagName.toLowerCase();
let outerHTML = focusedElement.outerHTML;
console.log(outerHTML); if (tag_name === "body") {
let xpath = getElementfullXPath(next_focusedElement);
let elements = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
if (elements && elements.snapshotLength === 1) {
console.log(`self.myWtClickEx(driver, By.XPATH, "${xpath}")`);
console.log("结束:tag名称为body");
console.log(`结束 父${father_level} -------------------------`);
return xpath;
} else {
console.log("结束:tag名称为body");
console.log(`结束 父${father_level} -------------------------`);
return null;
}
} let my_all_value = "";
let text = focusedElement.textContent.trim().replace(/"/g, "\\\"");
if (text !== "" && !text.includes("\\n")) {
my_all_value = `contains(text(),\'${text}\')`;
if (myDispose_count_number(text, "text", tag_name)) {
let xpath = `//${tag_name}[${my_all_value}]`;
let parameter = `driver, By.XPATH, "${xpath}"`;
myDispose_success(parameter);
return parameter;
}
} else {
text = ""
} let attributes = focusedElement.attributes;
console.log(`属性名称列表: ${Array.from(attributes).map(attr => attr.name).join(",")}`); for (let i = 0; i < attributes.length; i++) {
let attribute_name = attributes[i].name;
let attribute_value = attributes[i].value; if (attribute_name === "class") {
let class_value_list = attribute_value.split(" ");
console.log(`class列表:${class_value_list}`);
if (class_value_list.includes("focusing")) {
class_value_list = class_value_list.filter(value => value !== "focusing");
} for (let class_value of class_value_list) {
if (my_all_value === "") {
my_all_value = `contains(@class,"${class_value}")`;
} else {
my_all_value += ` and contains(@class,"${class_value}")`;
} if (myDispose_count_number(class_value, attribute_name, tag_name)) {
let parameter = `driver, By.CLASS_NAME, "${class_value}"`;
myDispose_success(parameter);
return parameter;
} let xpath = "";
if (text === "") {
xpath = `//${tag_name}[${my_all_value}]`;
} else {
xpath = `//${tag_name}[contains(text(),\'${text}\') and ${my_all_value}]`;
}
let result = myDispose_count_evaluate(xpath);
if (result) {
let parameter = `driver, By.XPATH, "${xpath}"`;
myDispose_success(parameter);
return parameter;
}
}
} else {
/*if (attribute_value === "" || /\d/.test(attribute_value)) {*/
if (attribute_value === "" || (attribute_name !== "src" && attribute_value.match(/[0-9]/))) {
continue;
} if (my_all_value === "") {
my_all_value = `contains(@${attribute_name}, "${attribute_value}")`;
} else {
my_all_value += ` and contains(@${attribute_name}, "${attribute_value}")`;
} if (myDispose_count_number(attribute_value, attribute_name, tag_name)) {
let xpath = `//${tag_name}[contains(@${attribute_name}, "${attribute_value}")]`;
let parameter = `driver, By.XPATH, "${xpath}"`;
myDispose_success(parameter);
return parameter;
} let xpath = "";
if (text === "") {
xpath = `//${tag_name}[${my_all_value}]`;
} else {
xpath = `//${tag_name}[contains(text(),\'${text}\') and ${my_all_value}]`;
}
let result = myDispose_count_evaluate(xpath);
if (result) {
let parameter = `driver, By.XPATH, "${xpath}"`;
myDispose_success(parameter);
return parameter;
}
}
} if (my_all_value !== "") {
let xpath = `//${tag_name}[${my_all_value}]`;
let result = myDispose_count_evaluate(xpath);
if (result) {
let parameter = `driver, By.XPATH, "${xpath}"`;
myDispose_success(parameter);
return parameter;
} else {
let textStr = `self.myWtClickEx(driver, By.XPATH, "${xpath}")`;
console.log("# 不是1");
console.log(textStr);
console.log(`结束 父${father_level} -------------------------`); if (father_level === 0) {
child_ctl = `father, By.XPATH, ".${xpath}"`;
child_ctl_tmp = `.${xpath}`;
next_focusedElement = focusedElement;
} let father = focusedElement.parentElement;
if (father) {
father_level++;
myDispose_click(father);
}
}
}
return null;
} function myDispose_success(parameter) {
if (father_level === 0) {
console.log(`self.myWtClickEx(${parameter})`);
} else {
console.log(`father = self.myWtFindElement(${parameter})`);
console.log(`self.myWtClickEx(${child_ctl})`);
}
console.log(`结束 父${father_level} -------------------------`);
} function myDispose_count_evaluate(xpath) {
let elements = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
if (father_level === 0) {
if (elements.snapshotLength === 1) {
return true
} else {
return null
}
} else {
if (elements.snapshotLength === 1) {
let firstElement = elements.snapshotItem(0);
let result = document.evaluate(child_ctl_tmp, firstElement, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
if (result.snapshotLength === 1) {
return true
} else {
return null
}
} else {
return null
}
}
} function myDispose_count_number(attribute_value, attribute_name, tag_name) {
if (attribute_value === "") {
return null;
};
if (attribute_name !== "text" && attribute_name !== "src" && attribute_value.match(/[0-9]/)) {
return null;
}; let xpath;
if (attribute_name !== "text") {
xpath = `//${tag_name}[contains(@${attribute_name}, "${attribute_value}")]`;
} else {
xpath = `//${tag_name}[contains(text(), \'${attribute_value}\')]`;
}; let result = myDispose_count_evaluate(xpath); if (result) {
console.log(`${attribute_name}:"${attribute_value}" 在网页中出现1次`);
return true;
} else {
console.log(`${attribute_name}:"${attribute_value}" 在网页中出现了多次`);
return null;
};
} function getElementfullXPath(element) {
if (element && element.id)
if (!element.id.match(/[0-9]/)) {
return \'//*[@id="\' + element.id + \'"]\';
} if (element==null)
return ""; var index = 0;
var loacl_tagName = element.tagName;
var sibling = element.previousSibling;
var sibling_tagName = null;
if (sibling) {
sibling_tagName = sibling.tagName;
}
while (sibling && sibling.nodeType === 1 && loacl_tagName === sibling_tagName) {
index++;
sibling = sibling.previousSibling;
if (sibling) {
sibling_tagName = sibling.tagName;
} else {
sibling_tagName = null;
}
} parent = element.parentNode;
if (parent) {
var xpath = getElementfullXPath(parent);
if (xpath === "undefined") {
return "";
} else {
if (index === 0) {
xpath += "/" + element.tagName.toLowerCase();
} else {
xpath += "/" + element.tagName.toLowerCase() + "[" + (index+1) + "]";
}
return xpath;
}
} else {
return "";
}
}
'; var input_textContent = '
let inputs = document.querySelectorAll(`input[type="text"]`);
inputs.forEach(input => {
input.addEventListener("input", function(event) {
console.log(this.value);
let parameter = myDispose_click(event.target);
if (parameter !== null) {
let textStr = `self.myWtSendKeysWebEx(${parameter}, "${this.value}")`;
console.log(textStr);
}
});
});
'; /*iframe*/
let iframes = document.getElementsByTagName('iframe');
for (let i = 0; i < iframes.length; i++) {
let iframe = iframes[i];
if (iframe.contentWindow && iframe.contentWindow.document) {
let script = iframe.contentWindow.document.createElement('script');
script.type = 'text/javascript';
script.textContent = click_textContent;
iframe.contentWindow.document.head.appendChild(script);
let inputs = iframe.contentWindow.document.querySelectorAll(`input[type="text"]`);
inputs.forEach(input => {
input.addEventListener("input", function(event) {
console.log(this.value);
let parameter = myDispose_click(event.target);
if (parameter !== null) {
let textStr = `self.myWtSendKeysWebEx(${parameter}, "${this.value}")`;
console.log(textStr);
}
});
});
}
} /*非iframe*/
let script = document.createElement('script');
script.type = 'text/javascript';
script.textContent = click_textContent + input_textContent;
document.head.appendChild(script);
界面自动化测试录制工具,让python selenium自动化测试脚本开发更加方便的更多相关文章
- Jenkins持续集成项目搭建与实践——基于Python Selenium自动化测试(自由风格)
Jenkins简介 Jenkins是Java编写的非常流行的持续集成(CI)服务,起源于Hudson项目.所以Jenkins和Hudson功能相似. Jenkins支持各种版本的控制工具,如CVS.S ...
- Python+selenium自动化测试中Windows窗口跳转方法
Python+selenium自动化测试中Windows窗口跳转方法 #第一种方法 #获得当前窗口 nowhandle=driver.current_window_handle #打开弹窗 drive ...
- 从零开始到设计Python+Selenium自动化测试框架-如何开始
如何开始学习web ui自动化测试?如何选择一门脚本语言?选择什么自动化测试工具? 本人已经做测试快5年,很惭愧,感觉积累不够,很多测试都不会,三年多功能测试,最近两年才开始接触和学习自动化测试.打算 ...
- 第一章 python+selenium自动化测试实战
@序章 自动化测试是软件测试的主流方向之一: 教程从测试的根本需求出发,讲解如何开展自动化测试. 首先,我们要明白,自动化仅仅是满足我们某种需求的一种工具:没有必要花费时间把它全部弄懂:我们只需要学会 ...
- python selenium 自动化测试web
如何使用python完成自动化测试web页面呢?首选selenium 那基于python的selenium如何使用,下面看一段测试案例: 基于python的selenium 安装方法: pip i ...
- python+selenium 自动化测试实战
一.前言: 之前的文章说过, 要写一篇自动化实战的文章, 这段时间比较忙再加回家过11一直没有更新博客,今天整理一下实战项目的代码共大家学习.(注:项目是针对我们公司内部系统的测试,只能内部网络访问, ...
- 在公司内部网络如何搭建Python+selenium自动化测试环境
在公司内部安装Python+selenium测试环境,由于不能连外网所以不能使用pip命令进行安装,经过多次尝试终于安装成功,现总结如下分享给大家,也希望跟大家一起学习和交流自动化网页测试时遇到的问题 ...
- python+selenium自动化测试_1
前言 回顾一下python+selenium基础,并整理相关知识点,分享给有需要,在前进道路上的朋友. print打印 #打印Hello World print("Hello World&q ...
- 《一头扎进》系列之Python+Selenium自动化测试框架实战篇6 - 价值好几K的框架,呦!这个框架还真牛叉哦!!!
1. 简介 本文开始介绍如何通过unittest来管理和执行测试用例,这一篇主要是介绍unittest下addTest()方法来加载测试用例到测试套件中去.用addTest()方法来加载我们测试用例到 ...
- python+selenium自动化测试环境搭建
selenium 是一个web的自动化测试工具,不少学习功能自动化的同学开始首选selenium ,相因为它相比QTP有诸多有点: * 免费,也不用再为破解QTP而大伤脑筋 * 小巧,对于不同的语 ...
随机推荐
- long数据类型跨平台问题
源代码 #include <iostream> int main() { std::cout << "size of long : " << s ...
- NOIP模拟70
T1 暴雨 解题思路 \(f_{i,j,k,0/1}\) 表示前 i 个铲平 j 个当前最高的是 k 并且当前是 奇数/偶数 的方案数. 由于只可以铲平 k 块,因此对于同一种 \(i,j\) 而言高 ...
- NOIP模拟56
前言 话说, T2 和 T3 的题面好像放反了. T1 爆零 解题思路 是个原题.. 当时 WindZR 25分钟就码完了,然后我就慌死,写完就开始调,诶,我当时场上不是切了吗,怎么现在打不对了.. ...
- itest(爱测试)开源接口测试&敏捷测试&极简项目管理 6.6.6 发布,新增接口mock
(一)itest 简介及更新说明 itest 开源敏捷测试管理,testOps 践行者,极简的任务管理,测试管理,缺陷管理,测试环境管理,接口测试,接口Mock 6合1,又有丰富的统计分析.可按测试包 ...
- 红白机基本原理(二) CPU
CPU 首发公号:Rand_cs NES 使用的 CPU 为 6502,但与标准的 6502 有些许不同,最大的不同在于 NES 使用的芯片拥有一个 pAPU(pseudo-Audio Process ...
- 解读surging 的内存过高的原因
前言 对于.NET开发人员来讲,一个程序占用内存过高,是极其糟糕,是一款不合格的程序软件,.NET开发人员也不会去使用服务器垃圾收集器(ServerGarbageCollection),而是选用工作站 ...
- StringUtils.join()方法使用
* StringUtils.join()方法使用 打印输出: * 使用 StringBuilder 进行拼接:张三,李四,王五 * 使用 StringUtils.join 进行拼接:张三,李四,王五 ...
- .Net8 AddKeyedScoped键值key注册服务异常
异常描述:This service descriptor is keyed. Your service provider may not support keyed services. 场景:.Net ...
- CF1523D Love-Hate
抽象化题意: 一共有 \(m\) 个元素,给定 \(n\) 个集合,每个集合的元素不超过 \(15\) 个,求出一个元素个数最多的集合 \(S\) 是至少 \(\lceil \dfrac{n}{2} ...
- P3355 骑士共存问题题解
题目链接:P3355 骑士共存问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题解: 棋盘问题考虑黑白染色成为二分图后做. 观察马的性质,可知一个点只能到一个异色点,所以,构造 ...