初探前端自动化测试--以 Vue 为例

随着前端项目规模日益膨胀,自动化测试显得越来越重要。在开发过程中就进行测试,可以保障代码质量和功能实现完整度,提升后期开发效率,也便于项目维护。当然,对于需求变更频繁、复用度低的内容,维护测试用例的成本就显得过高;但对于稳定、多次复用以及需要长期维护的项目,自动化测试就尤为重要。

现在优秀的开源项目大多都经过了各种测试。最近我也想码一个规范性要求严格的小项目,于是兵马未动粮草先行,先对前端自动化测试进行学习,总结记录一下学习成果,以在之后项目上进行实践。

前端的测试类型分为单元测试(unit testing)集成测试(integration testing)端到端测试(e2e testing)

  • 单元测试将应用拆分为细小的组件(JS 中的一个函数/对象/模块等)方法,然后针对这些方法进行单独的测试工作;
  • 集成测试是针对产品的某个功能的测试,又称功能测试;
  • e2e 测试则是从用户界面直达数据库的全链路测试,模拟用户进行页面操作,通过判断页面状态的变化来检查功能是否运行正常,需要用到浏览器环境。

开发模式

先绕点路,了解一下 TDD、BDD等以测试为导向的开发模式,有助于我们更深入地理解自动化测试。

TDD

TDD(Test Driven Development),即测试驱动开发。其先针对每个功能点抽象出接口代码,然后编写单元测试用例代码。之后实现接口,运行单元测试代码进行测试,循环往复直至所有单元测试通过。

TDD 要求测试先于编写功能代码。测试用例不仅是对于代码的验证,更成为对代码的规范与约束,确保在开发以及未来进行修改时能够极大程度地保证该模块行为仍然是正确的。

BDD

BDD(Behavior Driven Development),即行为驱动开发。BDD 和 TDD 一样,都要求先写测试,再写代码。可以认为 BDD 是 TDD 的一个子集或分支,是测试驱动开发的扩展。

TDD 基于开发者角度,重点测试函数的输入输出;而 BDD 更侧重于使用者角度,重点测试对用户行为的反应。

BDD 测试中,客户与开发者共同考虑系统该如何运行,然后用通用的语言抽象描述系统的行为,使得双方从技术层面和业务需求都能理解,避免沟通障碍。

测试工具

测试管理工具

用来组织和运行整个测试的工具,它能够将测试框架、断言库、测试浏览器、测试代码和被测试代码组织起来,并运行被测试代码进行测试。

可选择的有 Karma、Selenium、Mocha(Mocha既是测试工具,也是测试框架)。

测试框架

测试框架是单元测试的核心,提供了单元测试所需的各种API,并对测试用例分组。测试框架会抓取到代码抛出的 AssertionError 并提供附加信息。

选择较多,有名的有 Mocha、Jasmine等。不过 Mocha 既支持 TDD 也支持 BDD 的测试语法,Jasmine 只支持 BDD。

断言库

断言库提供了很多语义化的方法来对值做各种判断。当然也可以不用断言库,Node.js 中也可以直接使用原生 assert 库。

可供选择的断言库也很多,包括 should.js、expect.js、chai.js等。

测试浏览器

前端代码是运行在浏览器中的,要对其进行单元测试,只能将其运行在浏览器上。目前大部分测试工具都支持调用和运行本地浏览器来进行测试,但如果测试仅仅是针对函数和模块的单元测试,则完全可以使用一款无界面的 webkit 内核浏览器:PhantomJS。

测试覆盖率统计工具

测试覆盖率工具为代码在语法级分支上打点,之后运行代码,并在运行结束后根据收集到的信息统计出当前测试用例对源码的覆盖情况。

一般情况下你的测试管理工具会提供相关的覆盖率统计工具,但是有些情况下它们提供的工具未必是你想要的。比如当被测试的代码是经过了某些打包工具打包完了且被压缩和混淆了,同时打包工具还混入了很多自己的代码,这时覆盖率的统计就容易不准确。所以为了避免这种情况,测试覆盖率统计工具需要谨慎选择,至少你得确认它支持你的打包工具已经打包好的代码。

vue-cli 的配置

vue-cli 的 webpack 模版内置了开箱即用的 Karma + Jasmine 单元测试配置,以及 Nightwatch + Selenium E2E 测试配置,npm run test即可测试自带的一个初始测试用例。

vuecli-unit-test.jpg

vuecli-e2e-test.jpg

有坑请注意:

  1. 建议使用 cnpm 对 vue-cli 项目进行依赖安装,否则可能有些东西装不上。
  2. 自带的 e2e 测试要求装有 chrome 浏览器,否则报错Error retrieving a new session from the selenium server。我被这个坑折腾了半天…

开始编写测试代码

我的简历项目 是我花了一天时间写的一个 vue-cli 初始化、只用了 Vue 的一个小项目,我们拿这个简单的小项目来入门测试代码的编写。我们直接利用 vue-cli 的初始配置,即用 Karma + Jasmine 进行单元测试,Nightwatch + Selenium 进行 E2E 测试。

你可以将我的项目 clone 下来进行测试代码的编写练习,不过项目里的测试代码可能已经因为更新而何下面的示例不一样了。

单元测试

/test/unit/index.js中可以看到,单元测试的测试代码是/specs文件夹下以.spec.js结尾的文件。将自带的Hello.spec.js改写为LeftNav.spec.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import Vue from 'vue';
import LeftNav from '@/components/LeftNav';

describe('LeftNav.vue', () => {
const Constructor = Vue.extend(LeftNav);
const vm = new Constructor().$mount();

it('should render correct name', () => {
expect(vm.$el.querySelector('.leftNav h1').textContent)
.to.equal('Kyon Huang');
});

it('should render correct title', () => {
expect(vm.$el.querySelector('.leftNav h3').textContent)
.to.equal('Web 开发工程师');
});
});

E2E 测试

同理,将/test/e2e/specs/test.js改写为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// For authoring Nightwatch tests, see
// http://nightwatchjs.org/guide#usage

module.exports = {
'default e2e tests': function test(browser) {
// automatically uses dev Server port from /config.index.js
// default: http://localhost:8080
// see nightwatch.conf.js
const devServer = browser.globals.devServerURL;

browser
.url(devServer)
.waitForElementVisible('#app', 5000)
.assert.elementPresent('.leftNav')
.assert.containsText('h1', 'Kyon Huang')
.assert.elementCount('img', 1)
.end();
},
};

测试结果:

unit-test-result.png

e2e-test-result.png

可以看到,我们的单元测试和 e2e 测试都通过了。不过单元测试的覆盖率有点低,只有 25%。如何写好测试代码,需要进一步的学习。

结语

前端工程自动化入门系列下一篇:《再探前端自动化-持续集成》

参考资料

写作资料

开发人员看测试之TDD和BDD - JackieZheng - 博客园

前端自动化单元测试初探 - 简书

扩展阅读

虚拟座谈会:代码测试比率、测试驱动开发及行为驱动开发

PhantomJS 基础及示例 - 腾云阁 - 腾讯云