Node.js API Mocha 单元测试

一,什么是单元测试

单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个类,图形化的软件中可以指一个窗口或一个菜单等。总的来说,单元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。


二,为什么要进行单元测试

先说为什么浏览器可以直接访问,但为什么还要单元测试?第一浏览器访问其实是手工操作,简单的页面当然访问一下就好了,可是如果你要测的功能很复杂咋办?你要是忘了啥。手一滑操作错了咋办?
单元测试能确保程序底层逻辑大致上正确,而且一定程度上保证在你修改代码之后仍然逻辑正确,从而确保以后出bug几率更小,节省时间,从而节省钱。
也不一定非得写单元测试,你写一些shell脚本,使用curl也是可以自动化的。
我们把经过单元测试测过的模块组装在一起,显然更稳定也更有信心。


三,什么情况不能进行单元测试
很多PHP程序员写的程序就没有模块的概念,上来就从HTTP请求里读取参数,操作数据库,echo一个HTML片段或者JSON字符串,这样的代码要怎么测试?最多也就写成shell脚本测试,有一些PHP的程序员就算发现了重复代码,也是直接抽取到一个公用的文件里,所以这个文件容易堆积一堆小函数,这些函数严格来说都算不上是什么“单元”。


四,使用 Mocha 进行单元测试
Mocha是前端自动化测试框架,测试框架需要解决兼容不同风格断言库,测试用例分组,同步异步测试架构,生命周期钩子等框架级的能力。

Mocha的基本语法

var util = require("../server/util/tools");
var assert = require('assert');

// 测试手机号码
describe("测试手机号码", function () {
  it("测试手机号码", function(){
    var flag = util.checkMobile("13312341234");
    assert.equal(flag, true);
  });

  it("测试手机号码长度", function(){
    var flag = util.checkMobile("133123412341234");
    assert.equal(flag, false);
  });

});


异步测试语法

describe('现在要测XX功能', function () {
  it('某个变量的值应该是数字', function (done) {
    //写断言
    //手动调用done()表示异步结束,类似于Promise中的resolve
  })
});


不同风格的断言库
支持should.js,expect.js及node核心断言模块assert等。

生命周期钩子
生命周期钩子一般用来建立和清理环境或全局变量。

describe('hooks', function() {
  before(function() {
    // runs before all tests in this block
  });
  after(function() {
    // runs after all tests in this block
  });
  beforeEach(function() {
    // runs before each test in this block
  });
  afterEach(function() {
    // runs after each test in this block
  });
  // test cases
});

 

五,断言库 Chai

Chai是一个断言库合集,支持expect, assert, should断言语法,非专业测试岗位其实没必要深究,了解使用方法就可以了。

assert
TDD风格
API样例:assert("mike" == user.name);

should
BDD风格
API样例:foo.should.be("aa");

expect
BDD风格,基于should的简化
API样例:expect(foo).to.be("aa");

使用示例:

expect(bar).to.not.exist;//断言变量bar不存在
expect(data).to.have.ownProperty('length');//断言data有length属性
expect(name).to.be.a('string');//断言name是一个字符串
assert.equal(value1,value2);//断言value1和value2相等
Tim.should.be.an.instanceof(Person);//断言Tim是Person类的实例


上面的语法在引入了Chai后都是支持的,当断言不成立时,结果报告中会给出明确标记。当被测对象为undefined时,should就失效,而expect依然可以给出信息。

 

 

六, 基于Chai的自动化单元测试
单元测试的原理并不算复杂,相当于另外编写了一套程序,把业务逻辑中的脚本文件当做模块引入,模拟其运行环境(例如需要的浏览器类型,全局变量等),然后使用一组或若干组覆盖不同使用场景的参数来调用想要测试的函数单元,并判断函数返回的结果是否和预期的相同。

简单地说,自动化测试工具只是取代了一个照着Excel表格测试并记录结果的人力资源。

测试用例文件的基本写法:

var chai = require('chai');//引入断言库
var expect = chai.expect;//使用expect语法

//引用源代码中的业务逻辑模块;
var ColorFac = require('../../../../src/components/Example/colorFac');
describe("ColorFac Module Test", function () {
  it("should return a luminanced color", function () {
      //调用源代码中业务逻辑模块中的方法;
    var color = ColorFac.luminate("#fff", "-0.5");
      //编写测试断言
    expect(color).is.not.empty;
  });
});

单元测试报告:

 

七, 基于Chai-http的自动化接口测试
Chai-Http是基于Chai扩展的插件,可用于测试与http请求相关的逻辑代码。

let chai = require('chai');
let chaiHttp = require('chai-http');
let app = require('../server/app');
let should = chai.should();

chai.use(chaiHttp);

describe('Article', () => {
  describe('/文章列表相关', () => {
    it('获取文章列表', (done) => {
      chai.request(app)
        .get('/articlesss')
        .end((err, res) => {
          res.should.have.status(404);
          // res.text.should.equal('{"message":"token过期,请重新登录","resultCode":"403"}');
          done();
        });
    });
    it('获取文章列表', (done) => {
      chai.request(app)
        .get('/articles/10')
        .end((err, res) => {
          res.should.have.status(200);
          res.body.should.have.property('success');
          res.body.result.should.have.property('id');
          // res.should.have.property('success');
          // res.should.have.property('m');
          done();
        });
    });
  });

  describe('用户注册', () => {
    it('这里应该得到用户注册数据', (done) => {
      chai.request(app)
        .post('/auth/login')
        .set('content-type', 'application/x-www-form-urlencoded')
        .send({username:"13312341235", password:"1qazxsw2x", device_type:'mac'})
        .end((err,res) => {
          res.should.to.have.status(200);
          res.body.should.have.property('success');
          done();
        })
    }).timeout(3000);
  });

});

 



参考:

https://baike.baidu.com/item/%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95
https://www.zhihu.com/question/297987363/answer/509278649
https://www.cnblogs.com/dashnowords/p/9736491.html
https://www.cnblogs.com/dashnowords/p/9572753.html
https://github.com/chaijs/chai-http
https://www.jianshu.com/p/d6fc6d2a8901
https://github.com/tj/should.js

 

 

 

 

修改时间 2019-05-26

真诚赞赏,手留余香
赞赏
随机推荐
解读浮动闭合最佳方案:clearfix
Div保持高宽比
Photoshop 线稿上色技巧笔记
uniapp 开发笔记(一)
js获取选中的checkbox
“关于”中故事删减之前
Javascript实现长按按钮触发事件的方法
Wordpress 学习笔记 2 插件编写
微信小程序官方Demo登录失败,原因:PHP7.1以上版本废弃了Mcrypt
rem 单位手机页面适配