Macaca 学习笔记

前言

之前一直在 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 中。

下面直接上代码。

代码说明

driver 初始化

从 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 过来的,大家知道这么用就行了。

Driver API 说明

自己使用比较多且比较有用的 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 项目地址:

https://github.com/Vieira-zj/ZjJsAutomation


↙↙↙阅读原文可查看相关链接,并与作者交流