基于Node.js的UI自动化主流框架
若她涉世未深,就带她看尽人间繁华; 若她心已沧桑,就带她坐旋转木马。
文章尝试对基于Node的主流框架进行一个对比,从而"看尽繁华"。但是对比的指标和权重选取有一定的倾向性。不过,其中各个框架的试用,以及各个指标选取的背后思考还是可以借鉴参考。
1. 为什么是Node.js?而不是Java?
Java的好处:
- 传统的UI自动化基本就是Selenium为主导了,各种语言的版本都有,但是业内大部分是JAVA系统,所以还是Selenium-Java这一系列为主。
- 被测系统一般都是Java应用,各种中间件,数据准备API也是Java,便于调用。
Node的好处:
- Javascript对于前端更熟悉了,如果前端自测,无论是单元还是E2E,都更高效。
- 前后端分离以及基于Node版的中间件模块越来越多,在Node里调用也不是太大问题。
- 社区和新的框架层出不穷,脚本解释型编写调试效率略高一丢丢。
2. 成功入围选手
3. 一言不合就列KPI
重要指标
- 同步 vs. 异步
- 多浏览器支持
- Debug手段(单步调试)
- API扩展
- Page Object
- 语法简洁
- 用例管理
- 无线支持
权重标准
- 上面的指标,按照权重自上而下排列。
- 不过权重大小的标准是主观的,基于和Nightwatch互补,所以目标是同步、支持多浏览器和Debug方便的框架。
- 你完全可以设定不同的权重得出不同的目标框架。
4. 比武招亲正式开始
同步 vs. 异步
因为Node是异步的,所以所有框架原生都是异步的。为了解决callback的噩梦,有Promise等框架可以使得书写更加像同步那样。在这里我们说的同步是说不只是写起来,而是执行也是同步的,就和Java一样。
之所以看重这个指标,是因为:
- 现实中,其实大部分写UI自动化的人是测试同学。他们对于javascript和异步编程接触很少,对于异步的编写和调试往往呈现不适应,转而排斥Node系列的框架和归纳为UI自动化成本高这样的原因。
- 很多原有的UI自动化脚本是Java版的,年久失修以后,想拿来修修重用,转成异步发现一脸懵逼了。
- UI自动化比前端页面写javascript更多的异步逻辑。其实,由于浏览器和自动化脚本是两个进程,webdriver也是问答形式控制浏览器,那么异步编程就是自然而然的事情。相当于一直在和后端通过ajax接口交换数据,但是坏处就是稍复杂一些的用例逻辑,callback噩梦无法避免。
其实本身而言,同步异步只是一个适应的问题,并没有优劣之分。
比较
- WebdriverIO和Selenium-webdriver都通过插件将异步函数通过队列转化为同步执行管理。
WebdriverIO的例子
异步:
var webdriverio = require('webdriverio');
var options = { desiredCapabilities: { browserName: 'chrome' } };
var client = webdriverio.remote(options);
client
.init()
.url('https://www.taobao.com/')
.setValue('input.search-combobox-input', 'webdriverIO');
.click('button.btn-search')
.pause(2000);
.getTitle().then(function(title) {
console.log('Title is: ' + title);
})
.end();
同步:
describe('Taobao search', function() {
it('searches for WebdriverIO', function() {
browser.windowHandleSize({width: 1024, height: 800});
browser.url('https://www.taobao.com/');
browser.setValue('input.search-combobox-input', 'webdriverIO');
browser.click('button.btn-search');
browser.pause(3000);
// 同步的话,我可以变量得到返回值随意进行后续操作,而非callback。
var title = browser.getTitle();
console.log('Title is: ' + title);
//browser.end();
});
});
多浏览器支持
在电商的测试环境中,很多时候我们牵涉到多用户的浏览器操作,比如买卖家,助手问答,客服问答,问题双方等等。这时,解决方法有两种:
1. 一种是不断的切换账号达到一个浏览器多个账户的目的,但这显然是比较弱的,执行的观感也会冗余。
2. 另一种是自然的启动两个浏览器,在脚本里对不同的browser方便的做不同操作,必要时候进行同步,达到协作的目的。
比较
评价
虽然号称都支持,但其实支持的方式是不同的,便利程度也不同:
- Nighwatch是用同一个用例,并发执行用例的方式进行支持,如下面例子展示的一样。执行时通过参数可以决定有哪些浏览器启动,在脚本中通过环境变量进行分别操控,类似多线程编程的感觉。缺点就是整个用例充满了if else,像是硬把两个用例掺杂在了一起,异步再加上这种多线程的编写,给阅读调试都带来不小的难度。
- WebdriverIO是在配置里配好启动的浏览器名字,脚本里直接使用,因为是同步编程,所以就是按顺序执行了,相对更容易理解多了。
- 其他的框架都是类似的,driver或者browser是动态创建的,所以脚本里创建多少个就用多少个,也是非常简单直接的。
例子
- Nightwatch,具体参看官方例子 Testing WebRTC Apps with Nightwatch:执行命令
$ nightwatch --env chrome,firefox
脚本
module.exports = new (function() {
var firstClient = process.env.__NIGHTWATCH_ENV_KEY == 'chrome_1';
var testCases = this;
testCases['opening the browser and navigating to the url'] = function (client) {
client
.url('https://simplewebrtc.com/demo.html?nightwatchjs')
.waitForElementVisible('body', 1000);
};
if (firstClient) {
testCases['wait for clients to become connected'] = function(client) {
client
.waitForElementVisible('#localVideo', 1500)
.waitForClientConnected('#localVideo', 5000)
.waitForClientConnected('#remotes .videoContainer:nth-child(1) video', 8000,
'Remote video stream (%s) was connected in %s ms.');
};
testCases['wait for peer to disconnect'] = function (client) {
client
.pause(1000)
.waitForElementNotPresent('#remotes video', 10000);
};
} else {
testCases.suspend = function (client) {
client.pause(10000);
};
}
testCases.after = function(client) {
client.end();
};
})();
-
WebdriverIO:
配置:capabilities: { myChromeBrowser: { desiredCapabilities: { browserName: 'chrome' } }, myFirefoxBrowser: { desiredCapabilities: { browserName: 'firefox' } } },
脚本:
describe('Taobao search', function() { it('searches for WebdriverIO', function() { //两个浏览器都进行的操作 browser.windowHandleSize({width: 1024, height: 800}); browser.url('https://www.taobao.com/'); // Chrome单独操作,先执行 myChromeBrowser.setValue('input.search-combobox-input', 'Chrome'); myChromeBrowser.click('button.btn-search'); // Firefox单独操作,后执行 myFirefoxBrowser.setValue('input.search-combobox-input', 'Firefox'); myFirefoxBrowser.click('button.btn-search'); browser.pause(1000); var title = browser.getTitle(); console.log('Title is: ' + title); // outputs: "Title is: WebdriverIO (Software) at DuckDuckGo" browser.end(); }); });
Debug支持
异步情况下,对于Debug要求会高一些,最好能够像在IDE中一样单步调试。Node本身有node-inspector可以达到这样的效果,但是往往框架都会有自己的命令行环境,这种情况下,是否还能调用?没有尝试过改造的可行性,但是就从框架本身的介绍来看,我们发现WebdriverIO和Intern是支持的。
比较
例子
在配置里打开debug:true,启动后连接到node-inspector配置的端口即可。但是,如果同步编程的话,其实感觉一般的暂停debugger和log已经基本够用了,单步调试使用的场景不是很多。
API扩展
对于框架来讲,API级别能不能很好的扩展也是挺重要的,最起码我们要扩展一些登录、HSF调用等数据准备的公共函数。当然,因为是开源的,其实都可以修改源码的方式进行扩展,这种就不在此列了。
比较
Page Object
相对于API扩展,更贴合页面的一种抽象层次,复用的好可以减少很多页面元素定位操作的重复劳动。
比较
语法简洁
根本都是基于webdriver的协议,所以语法都类似,但是具体还是有些简洁的差别。从下面例子还是可以看出来,Nightwatch和WebdriverIO比较简洁,Intern是有些繁琐了。
比较
例子
client
.url('http://google.com')
.setValue('#q', 'webdriver')
.click('#btnG')
Notice how this is far simpler than with the original selenium-webdriverjs,
driver.get('http://www.google.com');
driver.findElement(webdriver.By.id('q')).sendKeys('webdriver');
driver.findElement(webdriver.By.id('btnG')).click();
and significantly simpler than with WD.js:
browser
.get("http://www.google.com")
.elementById('q')
.sendKeys('webdriver')
.elementById('btnG')
.click()
and GOD in intern.io:
this.remote
.get(require.toUrl('http://www.google.com'))
.findById('q')
.type('webdriver')
.end()
.findById('btnG')
.click()
.end()
For more details on the comparison between WebdriverIO, selenium-webdriverjs and WD.js, read this discussion.
用例管理
有很多优秀的测试框架可以和UI自动化框架结合,达到用例管理,结果展示等功能,比如Mocha、Jasmine等。这里是指框架本身已经做了很好的集成,或者本身自己提供了功能。
比较
无线支持
各个框架都是宣称支持webdriver for mobile的协议的,只是支持的API个数不同。如果能够很好支持,那就可以达到一个框架PC和无线通吃的便利性,但实际情况,大家无线还是倾向于专门的Appium等框架去做。
比较
总览
总评
- Selenium-webdriver和WD.js侧重UI,但封装和功能都比较少。
- Nightwatch和WebdriverIO各有千秋,社区活跃,友好性都不错。
- Intern更加侧重全测试方案,覆盖各个测试阶段,单元测试、功能测试等。
所以冠军就是
其实还有更多的选择
- Java型:Selenium-Java
- 录制型:AUI、Selenium-IDE
- 图像识别型:Skuli
和他们相比又有什么优劣呢?且听下回分解吧。