共 7717 字,读完需 10 分钟,速读需 5 分钟, 适合中高级前端工程师,本文对主流的 E2E 测试框架作了简单对比,指望对你们的技术选型有帮助。
测试,尤为是自动化测试在现代 WEB 工程中有着很是重要的角色,与交付过程集成良好的自动化测试流程能够在新版发布时帮你快速回归产品功能,也能够充当产品文档。测试因粒度不一样又能够分为单元测试、接口测试、功能测试。在 WEB 领域,功能测试亦称为端到端测试(End to End Test,简称 E2E 测试),笔者在本文中会结合自身实践和 GitHub 趋势对比最受欢迎的 Node.js E2E 测试解决方案,首先咱们按 GitHub 的 star 总数量排序,取前 5 名列举以下(注意:你阅读本文时 star 的数量可能已经不是最新的)。javascript
而后分别从环境搭建、测试编写、测试报告等方面来直观展现这 5 个 E2E 测试框架,指望可以对作测试框架选型的同窗有帮助。为了更客观的体现各测试框架的特色,笔者设计了一些包含 E2E 测试中经常使用操做的测试用例,分别使用不一样的框架来编写。E2E 测试的经常使用操做以下:css
CasperJS 是 star 数最高的测试框架,也是笔者最先开始采用的 E2E 测试框架,使用 Python 编写,虽不算是严格意义上的原生 Node.js 解决方案,但由于可以使用 npm 安装,且可以很好的与 Node.js 工具链组合使用,笔者仍是把它列在了这里。其特别之处在于只能与无界面浏览器(Headless Browser)组合使用,好比 PhantomJS 和 SlimerJS,这也让 CasperJS 的优点显而易见:测试运行速度比真实浏览器快很多,且你不须要在持续集成系统中安装各类浏览器或者某个浏览器的不一样版本;潜在的坑在于,无界面浏览器的表现有时和真实浏览器不彻底相同,会带来某些难以排查解决的浏览器兼容问题。html
若是使用 ES6 以前的风格来编写 CasperJS 测试,代码看起来会显得很是臃肿,而实际上 CasperJS 也不支持任何 ES6/ES7 的新语法,除非你在运行测试以前本身对代码进行预编译,实际代码以下:前端
casper.test.begin('Github Search', function suite(test) {
casper.start('https://github.com', function () { // 打开首页
test.assertVisible('.js-site-search-form', 'should search input visible');
this.fill('.js-site-search-form', { q: 'casperjs' }, true); // 键入搜索词、并提交
});
casper.then(function () {
test.assertEval(function() { // 确认搜索结果是 10
return __utils__.findAll('.repo-list-item').length >= 10;
}, 'should show 10 results');
});
casper.then(function () {
this.click('.repo-list-item h3 a'); // 点击第1条结果
});
var location = null;
casper.then(function () { // 这里是取环境变量
test.assertVisible('.repository-content', 'should repo detail visible');
location = this.evaluate(function () {
return window.location;
});
});
casper.then(function () { // 确认目前跳转到了 casperjs 官方仓库
test.assertEquals(location.pathname, '/casperjs/casperjs', 'should casperjs repo found');
});
casper.run(function () {
test.done();
});
});复制代码
由于 CasperJS 对 CoffeeScript 有自然的支持,熟悉 CoffeeScript 的同窗能够尝试使用 CoffeeScript 编写测试,或者使用这个工具转换为 CoffeScript。java
casperjs test casperjs/test.js
测试经过node
测试失败git
Protractor 是 Angular 官方正在使用的 E2E 测试框架,能够说是专门为 Angular 定制,内置了各类能够选择、操做 Angular 元素的便捷方法,若是你的应用基于 Angular 开发,使用它能够减小不少重复代码(显然相似的便利在其余框架中也有支持)。对于 Angular 的重度使用者,Protractor 会是很是明智的选择,不一样于 CasperJS 的是 Protractor 在真实浏览器中运行测试代码。此外,Protractor 内置的页面加载等待的功能,在 CasperJS 中须要本身设置合理的超时。相比于本文列出的其余框架,Protractor 的明显优点是测试用例的组织方式能够自由使用 Jasmine 或者 Mocha。angularjs
Protractor 默认开启了等待 Angular 加载并初始化完成的功能,若是你测试的不是 Angular 应用,则须要关闭这个功能,测试代码示例以下:github
describe('angularjs homepage todo list', function () {
browser.ignoreSynchronization = true; // 不启用智能等待,由于 github 不是用 angluar 编写的
browser.get('https://github.com');
it('should search input visible', function () {
var searchInput = element(by.className('js-site-search-focus'));
var searchForm = element(by.className('js-site-search-form'));
expect(searchInput.isDisplayed()).toEqual(true);
searchInput.sendKeys('protractor');
searchForm.submit();
});
it('should show 10 results', function () {
var searchList = element.all(by.className('repo-list-item'));
expect(searchList.count()).toEqual(10);
element(by.css('.repo-list-item h3 a')).click();
});
it('should repo detail visible', function () {
var repoContent = element.all(by.className('repository-content'));
expect(repoContent.isDisplayed()).toEqual([true]);
});
it('should protractor repo found', function (done) {
browser.executeScript(function () {
return window.location;
}).then(function (location) {
expect(location.pathname).toEqual('/angular/protractor');
done();
});
});
});复制代码
测试经过web
测试失败
一样流行的 Nightwatch,能够认为是 Protractor 的主要竞争对手,使用 Nigthwatch 编写的代码很是简洁,可是你须要手动在测试代码中添加合适的等待来保障测试的稳定,而 Protractor 和 TestCafe 则提供了内置的支持;Nightwatch 的主要劣势在于繁琐的安装步骤,可能部分同窗看到这个安装文档或者下面的安装步骤就知难而退了。
module.exports = {
'Github Search': function (browser) {
browser // 打开首页、填写搜索词、提交搜索表单
.url('https://github.com')
.assert.visible('.js-site-search-focus', 'should search input visible')
.setValue('.js-site-search-focus', 'nightwatch')
.submitForm('.js-site-search-form')
.pause(1000);
browser.execute(function () { // 确认展现 10 条搜索结果
return document.querySelectorAll('.repo-list-item').length;
}, function (args) {
browser.assert.equal(args.value, 10, 'should show 10 results');
});
browser.click('.repo-list-item h3 a').pause(1000);
browser.assert.visible('.repository-content', 'should repo detail visible');
browser.execute(function () {
return window.location;
}, function (args) { // 确认打开了 nightwatch 官网
browser.assert.equal(args.value.pathname, '/nightwatchjs/nightwatch', 'should nightwatch repo found');
});
browser.end();
}
};复制代码
测试经过
测试失败
TestCafe 是很是年轻但很受开发者欢迎的测试框架,由于不须要依赖 WebDriver 之类的东西,TestCafe 环境只需一键便可完成,这也意味着,你能够在任何安装了浏览器应用的物理设备上运行测试。TestCafe 对 ES6/ES7 语法的自然支持让它更具前瞻性,命令行工具产生的测试报告简洁但不失完整。因为开源的时间较短,相比于其余测试框架 TestCafe 的社区和生态还不够成熟。尽管如此,不断出现的各类 TestCafe 功能扩展也证实了它的社区和生态在不断壮大。对于站在 WEB 技术风口浪尖的同窗,TestCafe 无疑是很是值得留意的 E2E 测试解决方案,开箱即用的特性极大的下降了使用者的成本。
npm install testcafe -g复制代码
TestCafe 的测试组织方式详见这里,选择符支持也很是强大,支持相似于 jQuery 的灵活异步的选择符,断言风格很是相似 Chai,下面是测试代码:
import { Selector } from 'testcafe';
fixture `Github Search`
.page `https://github.com`;
test('should github search work as expected', async t => {
const searchInput = Selector('.js-site-search-focus');
const searchList = Selector('.repo-list-item');
const resultItem = Selector('.repo-list-item h3 a');
const repoContent = Selector('.repository-content');
await t.setTestSpeed(0.8);
await t.expect(searchInput.exists).eql(true, 'should search input visible');
await t.typeText(searchInput, 'testcafe');
await t.pressKey('enter');
await t.expect(searchList.count).eql(10, 'should show 10 results');
await t.click(resultItem);
await t.expect(repoContent.exists).eql(true, 'should repo detail visible');
const location = await t.eval(() => window.location);
await t.expect(location.pathname).eql('/DevExpress/testcafe', 'should testcafe repo found');
});复制代码
testcafe chrome testcafe/test.js复制代码
测试经过
测试失败
CodeceptJs 可能并不算是严格意义的 E2E 测试框架,它对各类测试运行工具作了一层封装,旨在提供更简洁的 API,你能够自由选择下面这些测试运行工具:
CodeceptJs 让笔者比较欣赏的地方在于测试用例的组织,基于 Feature 和 Scenario 两个粒度来组织测试让它看起来更有 E2E 测试的样子,它支持最新的 ES6 语法,同时也屏蔽各类复杂的回调细节,全部的测试用例都是以第一人称来作,让测试代码阅读起来更像是天然语言,而让笔者担心的地方在于,过多的封装可能致使出问题时排查比较困难。
Feature('Github Search');
Scenario('search codecept repo', (I) => {
I.amOnPage('https://github.com');
I.seeElement('.js-site-search-focus');
I.fillField('.js-site-search-focus', 'codeceptjs');
I.pressKey('Enter');
I.seeElement('.repo-list-item');
I.click('.repo-list-item h3 a');
I.seeElement('.repository-content');
I.seeInCurrentUrl('/Codeception/CodeceptJS');
});复制代码
复制代码Feature('Github Search');
Scenario('search codecept repo', (I) => {
I.amOnPage('https://github.com');
I.seeElement('.js-site-search-focus');
I.fillField('.js-site-search-focus', 'codeceptjs');
I.pressKey('Enter');
I.seeElement('.repo-list-item');
I.click('.repo-list-item h3 a');
I.seeElement('.repository-content');
I.seeInCurrentUrl('/Codeception/CodeceptJS');
});复制代码
codeceptjs run复制代码
查看报告
测试经过
测试失败
本文中的全部代码能够在 GitHub 仓库上看到。任何开发工具的演化都是朝着更快捷、高效的目标。本文介绍的几个 E2E 测试框架能够说各有所长,在作框架选型的时候该考虑哪些因子呢?这些因子的优先级如何?下面是笔者的考虑:
若是你的项目中须要添加 E2E 测试,作决定的时候没有标准答案,由于还须要结合项目自身的特色,好比规模大小、对上面各因子的要求。
备注:本文的初始版原本源于 Medium 上的文章,可是笔者在原文基础上从新设计了测试用例,每一个测试框架的介绍也参与了笔者自身的使用经验,框架选型上也融入了本身的思考,有兴趣的能够去看原文。
本文做者王仕军,商业转载请联系做者得到受权,非商业转载请注明出处。若是对文中的内容有任何疑问,欢迎留言讨论。想知道我接下来会写些什么?欢迎订阅个人专栏,点击文章下方的关注便可。