测试框架Mocha使用

没头脑

作者 没头脑

创建时间 2021-03-15

更新时间 2021-06-17

阅读 151

评论 0

Mocha是一个功能丰富的javascript测试框架,运行在node.js和浏览器中,使异步测试变得简单有趣。为Javascript应用程序添加测试,不仅可以保证代码的质量,还可以获得一个像这样的小徽章 测试覆盖率报告 。除了Mocha,JestAvaJasmine等都是不错的选择。 之前写过的一个小项目,就是使用Mocha测试的,可以当作实战参考。 如果你还在纠结,怎么使用Webpack在本地创建一个项目,可以参考上一篇文章webpack入门

一、安装

1.1、安装Mocha

# 作为项目的依赖安装
npm install --save-dev mocha
# 全局安装
npm install --global mocha

1.2、安装chai

chai是用于node和浏览器的BDD/TDD断言库,可以和任何测试框架完美搭配。

npm install chai

1.3、配置package.json

{
 "scripts": {
    "test": " mocha --exit"
  }
}

二、编写测试脚本

首先,需要一个被测试的模块 src/sum.js,内容如下

function sum (a, b) {
    return a + b
}
module.exports = sum

要检测sum模块的功能是否符合我们的预期,需要编写一个测试脚本进 test/index.js进行验证。

const sum = require(''../src/sum.js)
const assert = require('assert')
const assert = require('assert')

describe('Sum test', function () {
    it('test 1 + 9', function (done) {    
        assert.equal(sum, 9)
    })
})
  • describe方法用于描述测试套件(test suit)的结构。describe下需要包含至少一个itdescribe声明。
  • it用于标识每个单独的测试,但it并不会告诉mocha关于测试套件的组成。
  • chai有三种断言风格expectassertshould。你可以选择自己喜欢的一种。
  • assert.equal(actual, expected, [message])
    • @param { Mixed } actual 实际的结果
    • @param { Mixed } expected 期望的结果
    • @param { String } message 可选参数,消息
      断言 actual == expected,如果actual不等于expected,则会在测试过程中抛出一个错误Error: Uncaught [AssertionError: 9 == 10]
      然后运行测试
    npm run test
    

    2.1、异步代码

    it() 回调中添加一个形参(通常是 done ),mocha就会等待done被调用,然后才结束测试。done会接受一个Error实例或一个false值,使用其他的任何值都是无效的,并且会抛出一个错误(通常会导致测试失败)。
    describe('User', function() {
      describe('#save()', function() {
        it('should save without error', function(done) {
          var user = new User('Luna');
          user.save(function(err) {
            if (err) done(err);
            else done();
          });
        });
      });
    });
    

    2.1.1、使用async和await

    如果你的环境支持async和await,你也可以像这样编写一个异步测试
    beforeEach(async function() {
      await db.clear();
      await db.save([tobi, loki, jane]);
    });
    describe('#find()', function() {
      it('responds with matching records', async function() {
        const users = await db.find({type: 'User'});
        users.should.have.length(3);
      });
    });
    

    2.1.2、使用Promise

    你也可以return一个Promise来替代done的功能。
    const assert = require('assert');
    // antipattern
    it('should complete this test', function(done) {
      return new Promise(function(resolve) {
        assert.ok(true);
        resolve();
      });
    });
    
    在v3.3.0版本或更高的版本中,如果你同时使用了promisedone,将会导致异常。

    三、配置mocha

    在项目的根目录中创建一个文件 .mocharc.json
    {
      "require": ["@babel/register"],
      "file": null,
      "watch": true
    }
    
  • file:array|string,默认是Null。 明确指定在加载测试文件之前,加载的文件。例如,你想在其他所有测试文件的每次测试之前,运行钩子函数时,非常有用 }
  • require:array,默认是null。在加载用户界面或是测试文件之前,require一个模块。mocha默认是不支持ES6语法的,如果你使用ES6或更高的语法编写测试用例,你需要安装babel,然后修改require属性。
  • watch:boolean。监听当前文件夹下的文件,如果有文件被修改,就会重新运行测试
  • watch-files:array。指定监听的文件。
  • watch-ingore:array,默认["node_modules",".git"]。不被监听的路径。
  • reporter:指定测试报告的格式,默认是 "spec" ,可选值: "tap""doc"
    这些配置也可以在package.json中指定
    {
     "scripts": {
        "test": "mocha --require @babel/register --watch"
    }
    }
    

    四、Hooks

    Mocha提供了 before() , after(), beforeEach()afterEach() 钩子函数。这些函数用于设置准备测试的前提条件,并在测试后进行清理工作。
    describe('hooks', function() {
      before(function() {
        // 在此作用域的第一个测试之前运行一次
      });
      after(function() {
        // 在此作用域的最后一个测试之后运行一次
      });
      beforeEach(function() {
        // 在此作用域的每个测试运行之前运行
      });
      // 你也可以为钩子函数定义一个名称
      afterEach('Custom name', function() {
        // 在此作用域的每个测试运行之之后运行
      });
      // test cases
      it('test', function () {
        // test
      })
    });
    

    4.1、异步hooks

before() , after(), beforeEach()afterEach() 钩子函数也可以同步或异步执行。例如,在每个测试开始之前,你需要使用虚拟数据填充数据库。

describe('Connection', function() {
  var db = new Connection(),
    tobi = new User('tobi'),
    loki = new User('loki'),
    jane = new User('jane');

  beforeEach(function(done) {
    db.clear(function(err) {
      if (err) return done(err);
      db.save([tobi, loki, jane], done);
    });
  });

  describe('#find()', function() {
    it('respond with matching records', function(done) {
      db.find({type: 'User'}, function(err, res) {
        if (err) return done(err);
        res.should.have.length(3);
        done();
      });
    });
  });
});

在上面的例子中,beforeEach指定了一个形参done,mocha就会等待done被调用,然后开始执行测试用例。

五、chai的assert断言风格

chai.***(actual, expected, [message])

  • @param { Mixed } actual 期望值
  • @param { Mixed } expected 实际获得的值
  • @param { String } message 可选,消息 断言 actual == expected
    const assert = require('assert')
    // 断言 actual == expected
    assert.equal(3, '3', '== coerces values to strings');
    // 断言 actual != expected
    assert.notEqual(3, 4, 'these numbers are not equal');
    // 断言actual === expected
    assert.strictEqual(true, true);
    // 断言actual !== expected
    assert.notStrictEqual(3, '3');
    // 深度比较两个对象
    assert.deepEqual({ tea: 'green' }, { tea: 'green' });
    // 深度比较两个对象
    assert.notDeepEqual({ tea: 'green' }, { tea: 'jasmine' });
    // 判断值是否是true
    assert.isTrue(teaServed, 'the tea has been served');
    // 判断值不是true
    assert.isNotTrue(teaServed, 'the tea has been served');
    // 判断值是一个函数
    function serveTea() { return 'cup of tea'; };
    assert.isFunction(serveTea, 'great, we can have tea now');
    // isBoolean isNotBoolean isNotNumber isNumber isNotString isString isNotArray isArray isObject isNotObject同上
    //----------------------------------------------
    // include查询元素的时候,使用的是===
    assert.include([1,2,3], 2);
    assert.include('foobar', 'foo');
    assert.include({ foo: 'bar', hello: 'universe' }, { foo: 'bar' }, 'object contains property');
    // 断言值匹配正则表达式
    assert.match('foobar', /^foo/');
    // 断言值不匹配正则表达式
    assert.notMatch('foobar', /^foo/, 'regexp does not match');
    

    六、使用JSDOM

    Node环境是没有window对象和dom对象的,如果你要测试的项目中需要和DOM交互,你就会需要JSDOM了。
    npm install --save-dev --save-exact jsdom jsdom-global
    
    然后创建一个文件test/generate-page.js
    const jsdom = require('jsdom')
    const { JSDOM } = jsdom
    const template = `<!DOCTYPE html>
    <html font-size="62.5%"  lang="zh">
    <head>
    </head>
    <body>
      <div>blog1997</div>
      <div class="canvas">
          <a><img class="lazy" data-src=""/></a>
          <a><img class="lazy" data-src=""/></a>
          <a><img class="lazy" data-src=""/></a>
          <ul>
              <li>1</li>
              <li>2</li>
              <li>3</li>
          </ul>
      </div>
    </body>
    </html>`
    global.jsdom = new JSDOM(template, {
      resources: 'usable',  // 允许执行script标签中的脚本,只有配合 runScripts: "dangerously"才可用
      pretendToBeVisual: true, // 生成window.cancelAnimationFrame()和window.requestAnimationFrame()方法
      runScripts: "dangerously"
    })
    global.window = global.jsdom.window
    global.document = global.jsdom.window.document
    global.div = global.document.querySelector('div')
    
    然后修改 .mocharc.json
    {
      "require": ["@babel/register", "jsdom-global/register", "./test/generate-page.js"]
    }
    
    这样,你的测试用例就可以和DOM交互了
    describe('easing test', function () {
      it('test swing', function (done) {
          Animate(document.querySelector('div'), {
              width: '126px',
              easing: 'swing'
          }, (el) => {
              assert.equal(window.getComputedStyle(el).width, '126px')
              done()
          })
      })
    }
    

    七、生成测试覆盖率报告

    首先安装nyc
    npm i nyc --save-dev
    
    配置package.json
    {
      "scripts": {
        "test": " mocha",
        "coverage": "cross-env NODE_ENV=test nyc --reporter=lcov npm run test"
      }
    }
    
  • --reporter:配置生成测试覆盖率报告的格式,可以指定多个
    然后运行命令
    npm run coverage
    
    之后就会在项目根目录中创建一个文件夹coverage,里面存放了测试覆盖率的内容

    八、生成测试覆盖率报告的徽章

    要生成一个测试覆盖率的徽章,首先,你需要将你的代码上传到GIT上,然后通过codecov平台(国内访问有时候可能比较慢),获取一个徽章。
    如图所示,点击add new repository,将你的项目添加到codecov平台上。 然后我们会得到一个像这样的token
    CODECOV_TOKEN='1f721be7-4609-4cae-1433-3a84d1d8b2df'
    

    8.1、安装codecov

    npm install -g codecov
    
    然后添加codecov的配置文件codecov.yml
    parsers:
      javascript:
        enable_partials: yes
    coverage:
      range: 70..100
      round: down
      precision: 2
    codecov:
    token: 你的token
    
    修改package.json
    {
    "scripts": {
      "test": " mocha",
      "coverage": "cross-env NODE_ENV=test nyc --reporter=lcov npm run test",
      "report-coverage": "codecov"
      }
    }
    
    运行命令
    npm run coverage
    npm run report-coverage
    
    然后测试覆盖率就上传到codecov中去了,点击setting > badge。就可以获取小徽章了

提 交
暂无评论