之前一直在 TesterHome 潜水,受益非浅,所以也决定写点东西与大家分享。
因工作关系,最近开始学习 Macaca, 在网上找了一下,没有文章详细地介绍 Macaca, 自己主要是参考官方的例子(很全面,但入门有点难),所以在此总结一下。
开发环境:Mac OS + Visual Studio Code + Macaca 2.0.9 + Chrome 61.0.xxxx
语言:JavaScript.
Macaca 环境配置确实是有些麻烦,但不难,网上介绍文章很多,在这就不说了。
项目中比较重要目录和文件(Git 库地址见文章最后):
Mocha-test: 包括 Mocha 和 Should 的测试 demo. 因为 Macaca 默认是基于 Mocha 来执行的,断言我使用的是 Should.
Macaca-test: 包括 Macaca 测试用例 demo.
run-test.sh: 测试执行脚本,具体配置在 Makefile 中。
下面直接上代码。
从 wd (web driver) 获得一个 driver 对象。
var driver = wd.promiseChainRemote({
host: 'localhost',
port: process.env.MACACA_SERVER_PORT || 3456
});
初始化 driver 对象。
return driver
.init({
platformName: 'desktop',
browserName: testConsts.envVars.browser,
userAgent: 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0 Safari/537.36 Macaca Custom UserAgent',
deviceScaleFactor: 2
})
.setWindowSize(1280, 800);
以上直接从官方例子中 copy 过来的,大家知道这么用就行了。
自己使用比较多且比较有用的 API, 包括如下几个:
1) waitForElement
function waitForElement(using, value, asserter, timeout, interval) { … }
查找元素,默认 timeout=1000ms, interval=200ms. 比如 waitForElementById(), waitForElementByCssSelector().
一般加一个元素的定位条件就可以使用,我比较喜欢按如下方式调用:
.waitForElementById('kw', wd.asserters.isDisplayed, 8 * 1000, 500)
即指明 waitFor 的条件是 isDisplayed, 然后 timeOut 和 interval 根据实现情况自定义。
asserters 条件包括:isDisplayed, nonEmptyText, textInclude(text), jsCondition(jsExpr)
但是使用 jsCondition(jsExpr) 的过程中一直会 pending, 不知道是什么原因!
注:因为 Macaca 用例的执行是基于事件驱动的,而不是实际摸拟操作,所以执行的速度非常快,所以尽量使用 waitForElementById 函数,而不是直接使用 elementById 函数,确保元素可见之后再操作。个人使用一段时间感觉元素定位还是非常稳定的。
2) waitForElements
调用方式与 waitForElement 函数相似,但因为 Macaca 是链式调用,所以 waitForElements 的使用方法不同,如下:
.waitForElementsByCssSelector('input[type="text"]')
.then(function (els) {
console.log('element input count:', els.length);
return els[0];
})
.sendKeys('test')
3) execute(jsExpr)
向当前页面注入 js 脚本并执行。比如执行 mouse hover 操作:
// 使用原生JS
.execute(
`
var element = document.querySelector('${cssSelector}');
var event = document.createEvent('MouseEvent');
event.initMouseEvent('mouseover', true, true);
element.dispatchEvent(event);
`
)
// 使用JQuery
.execute(`$('a[name="tj_settingicon"]').mouseover();`);
另外一个比较好用的功能是执行 JS 脚本后,可获得返回值。如下脚本取一个元素的 text 并打印。
(注:我只验证过简单类型,如 string, boolean, 对象类型未验证)
// 获取元素Text
.execute(`
var uiElement = document.getElementById('setf');
uiElement.style.backgroundColor="#000000";
return uiElement.innerText;`)
.then(value => console.log('link text by JS return:', value))
// 使用JQuery获取元素属性值
.execute(`return $('input#su').attr('value');`)
现在浏览器基本都支持 JQuery, 所以尽量使用 JQuery, 要简单很多。
4) 模拟键盘操作
常用的 keycodes:
const keyCodes = {
Backspace: '\uE003',
Tab: '\uE004',
Enter: '\uE007',
Shift: '\uE008',
Control: '\uE009',
ArrowLeft: '\uE012',
ArrowUp: '\uE013',
ArrowRight: '\uE014',
ArrowDown: '\uE015',
Delete: '\uE05D',
};
使用:
.keys(testConsts.keyCodes.Enter)
.keys(testConsts.keyCodes.Backspace)
.keys(testConsts.keyCodes.Shift + testConsts.keyCodes.ArrowLeft)
注:发送键盘事件在我本地测试只能使用 keys(), 使用 sendKeys() 无效。
下面是一个使用图片对比验证的例子。
const diffImage = require('./utils.js').diffImage;
it('#3, verification by diff image', function () {
return driver
.get(initialURL)
.sleep(testConsts.waitTime.shortWait)
// .customSaveScreenshot(this) // save error baseline
.sleep(testConsts.waitTime.shortWait)
.openBaiduLoginDialog()
// .customSaveScreenshot(this) // save origin baseline
.takeScreenshot()
.then(imgData => {
const screenshotFolder = path.resolve(__dirname, '../screenshots');
const originImgPath = path.join(screenshotFolder, 'origin.png');
fs.exists(originImgPath, function (exists) {
exists.should.be.ok('origin image exist.');
}); // Warn: sync function
const newImg = new Buffer(imgData, 'base64');
fs.writeFileSync(path.join(screenshotFolder, 'new.png'), newImg.toString('binary'), 'binary');
const diffImgPath = path.join(screenshotFolder, 'diff.png');
return diffImage(originImgPath, newImg, 0.1, diffImgPath);
})
.then(result => {
result.should.be.true('image diff.');
})
.catch(e => {
console.error(e);
});
});
结果是基于对比 origin 图片和 actual 图片差异的 diff 图片。图片对比为像素级别,个人觉得是非常强大,非常方便的一个功能。
大家可以看到,Macaca JS 是使用函数式编程,因此不能用传统的方式来添加自定义函数。添加自定义函数要使用如下方式:
wd.addPromiseChainMethod('returnHelloMessage', function () {
return 'hello world';
});
第一个参数为自定义函数的名称;第二个参数是一个回调函数,里面为具体的函数功能。
先写这么多,主要写了一些自己觉得比较有用的东西,其实还有很多细节上的东西,完整的例子大家可以自己看代码。项目中,自动化测试用例大部分都是基于 Baidu 首页来写的,本地都能执行通过。但因为是 demo, 有些不严谨的地方大家就别纠结了,呵!
非常感谢大家耐心读完本篇文章,欢迎提问,谢谢!
最后贴上 Git 项目地址: