前言

常用的测试框架有 Karma、mocha, 其中 Karma 是一个以浏览器为引擎的测试,而 mocha 用的是 Node.js, 淘宝推出了一个类 Karma 的开源框架叫 Totoro。

由于 Karma 是使用真实的浏览器环境,并且可以测试兼容性,我们采用 Karma 作为框架。

常用的断言库:

  • Node.js 的 assert
  • Jasmine
  • expect
  • Chai

和 Karma 比较配套的是 Jasmine。

安装 Karma + Jasmine

npm i karma jasmine-core karma-jasmine karma-chrome-launcher -D

npm i karma -g

安装一个全局的 karma 命令,然后执行 karma init 生成 karrna.config.js

karma init

一路回车就好

然后新建一个 src 目录写源文件(src/util.js)

var util = {
  reverse(str) {
    return str
      .split('')
      .reverse()
      .join('');
  }
};

然后写测试文件(test/util-test.js)

describe('reverse', function() {
  it('reverse word', function() {
    expect(util.reverse('abc')).toEqual('cba');
  });
});

接下来把文件添加到 karma.config.js 里面

module.exports = function(config) {
  config.set({
    ...
    // list of files / patterns to load in the browser
    files: [
      'test/*.js',
      'src/*.js'
    ],
    ...
  })
}

告诉它要在浏览器加载哪些文件,然后运行 karma start 执行测试,如果遇到报 karma 的模块找不到的情况,则可以把找不到的模块安装成全局的。

成功运行后,终端将会输出测试结果

浏览器也会打出结果

我们修改下代码再看下不通过的情况

describe('reverse', function() {
  it('reverse word', function() {
    expect(util.reverse('abc')).toEqual('abc');
  });
});

这样就实现了一个最基本的单元测试,现在来看一下测试的覆盖率。

测试覆盖率报告

一般测试的覆盖率要越高越好,Karma 支持查看测试代码的覆盖率, 安装一个包:

npm install karma-coverage -D

然后在 karma.config.js 里面添加配置

preprocessors: {
  'src/*.js': ['coverage']
},

// add
coverageReporter: {
  type: 'html',
  dir: 'coverage/'
},

reporters: ['progress', 'coverage'],

添加一个预处理,告诉它 src 下的源文件需要用 coverage 预处理一下,然后生成的 report 放在 coverage 目录下面,用 HTML 的形式。

重新运行 karma start, 将会生成 HTML 文件,打开这个 HTML 文件就可以看到覆盖率报告。

覆蓝率为 100%, 我们给源 util 添加一个逻辑分支

var util = {
  reverse(str) {
    if (str.length <= 1) return str;
    return str
      .split('')
      .reverse()
      .join('');
  }
};

然后再看覆盖率报告

可以看到,分支变成了 50%

再添加一个分支:

var util = {
  reverse(str) {
    if (typeof str !== 'string')
      throw 'util.reverse should pass a string argument';
    if (str.length <= 1) return str;
    else
      return str
        .split('')
        .reverse()
        .join('');
  }
};

点进 util 也可以看到哪些代码没有覆盖到:

在 util-test.js 里面添加测试代码

describe('reverse', function() {
  it('reverse word', function() {
    expect(util.reverse('abc')).toEqual('cba');
  });

  it('reverse字符串长度为1时返回自已', function() {
    expect(util.reverse('a')).toBe('a');
  });

  it('reverse传值不是字符串时会抛异常', function() {
    expect(function() {
      util.reverse(null);
    }).toThrow();
  });
});

这时候看到覆盖率就成 100%了。

覆盖率实现原理

从浏览器加载的 util.js 中可以看到一段代码

可以看到总共被改成了 4 个分支,每个分支如果有执行就会顺带着执行 ++ 操作。这样它就可以统计到有多少代码被执行到了。

Jasmine 提供的断言 API 除了上面 toEqual/toBe/toThrow 之外, 具体还有

expect(result).toBeDefined();
expect(result).toBeGreaterThan(3);
expect(result).toBeLessThan(0);
expect(thing).toBeNaN();
expect(string).toContain(substring);
expect('my string').toMatch(/^my/);

自动化测试

自动化测试一般用 E2E 测试,即端到端测试。它的工具也有几种

  • PhontomJS
    • 无界面浏览器
  • Senlenium
    • 打开电脑的浏览器运行,支持多语言操控
  • Protractor
    • Senlenium 的一个 JS 容器

我们使用 Protractor, 因为它提供了-些方便的操控浏览器的 API 以及断言库。

首先安装 protractor:

npm i protractor -g

webdriver-manager update
webdriver-manager start

然后写一个 conf.js

exports.config = {
  seleniumAddress: 'http://localhost:4444/wd/hub',
  specs: ['test-spec.js']
};

test-spec.js

describe('site', function() {
  it('登录框正常使用', function() {
    browser.waitForAngularEnabled(false);
    browser.get('https://test.com');
    expect(browser.getTitle()).toEqual('Search Listings in Las Vegas - tes');
    $$('nav .sign-icon + li.sign-in').click();
    expect($$('.sign-log').count()).toEqual(1);
    $$('.sign-log input[name=account]').sendKeys('yin@abc.com');
    $$('.sign-log input[name=password]').sendKeys('3345983893');
    $$('.sign-log input[type=submit]').click();
    browser.driver.sleep(1000);
    expect($$('.sign-log').count()).toEqual(0);
  });
});

我们测试登录框能否正常使用,先调 browser.get 打开一个网页,在 load 完成之后会继续执行下面的逻辑:先单击导航的 sign-in 弹出登录框,然后往两个输入框填入账号密码,再点提交按钮,让浏览器等待 1s, 最后检测弹框是否消失了。因为注册完会刷新页面。

然后运行 protractor start, 它就会打开浏览器,自动打开网页,按照我们的设定执行。

总结

首先看一下工程目录

- website
  - karma.config.js
  - unit-test
    - index.js
    - mock
      - middleware.js
      - home.js
    - test
      - sign-log-test.js
      - util-test.js
    - coverage
      - html/index.html
  - e2e-test
    - conf.js
    - spec
      - test-login.js

要尽可能地提高覆盖率,并且测试要尽可能稳定,理想状态是多个版本迭代还能待续使用,每次上线前或者改完代码后都可以跑一下单元测试。对一些复杂、关键的操作使用自动化测试。

自动化测试写起来比较简单,就是比较烦琐。