Skip to content

Latest commit

 

History

History
372 lines (286 loc) · 9.4 KB

mocha使用教程.md

File metadata and controls

372 lines (286 loc) · 9.4 KB

mocha使用教程

mocha是一个javascript测试框架,可以将测试的异常映射到对应的测试用例上。

基本使用

  • 安装
# 安装mocha
npm install mocha -D
  • 添加测试用例

mocha默认会对项目根目录下的test文件夹下面的所有.js文件进行测试

# 创建测试文件夹
mkdir test

test文件夹下创建demo.js文件,写入一段基本的测试用例代码:

// demo.js
const assert = require('assert');

// 定义一组测试套件
describe('start a mocha test suit.', function() {
  // 定义一个测试用例
  it('start a first test.', function() {
    assert.equal(1, 1);
  });
});
  • 配置npm命令
{
    "scripts": {
        "test": "mocha"
    }
}

配置好npm命令后运行npm test即可以运行测试用例了,结果如下图所示:

image

  • describe() 表示一组测试套件(一组测试用例)
    • 第一个参数是测试套件描述
    • 第二个参数是一个函数,可以在该函数内添加具体的测试用例
  • it() 表示一个测试用例
    • 第一个参数是该测试用例的描述
    • 第二个参数是一个函数,在函数内部具体实现测试用例内容

参数配置

  • 通过命令行参数修改配置

命令行的使用方式是--key=value的格式,如下所示,设置超时时间为3000ms:

"scripts": {
    "test": "mocha --timeout=3000"
},
  • 通过配置文件修改参数

mocha支持多种配置文件格式,在mocha6.0.0版本及以后,支持正在项目根目录创建.mocharc.js | .mocharc.cjs.mocharc.json | .mocharc.jsonc.mocharc.yaml | .mocharc.yml。下面演示json的配置方式,其文件内容是mocha参数的默认配置:

{
  "diff": true,
  "extension": ["js", "cjs", "mjs"],
  "package": "./package.json",
  "reporter": "spec",
  "slow": "75",
  "timeout": "2000",
  "ui": "bdd",
  "watch-files": ["lib/**/*.js", "test/**/*.js"],
  "watch-ignore": ["lib/vendor"]
}

更多配置文件写法可以查阅这里

6.0.0以前的版本,配置文件是存放在test目录下的mocha.opts中,具体的翻阅对应版本的文档。

  • 修改配置文件位置

可以在使用mocha命令时添加命令行参数--config [your path]指定配置文件路径:

{
    "scripts": {
        "test": "mocha --config ./a.json"
    },
}

接口风格

mocha运行开发者选择自己喜欢的风格的接口模式,支持BDDTDDExportsQUnitRequire风格的接口。默认使用的是BDD风格。

可以通过修改配置参数来修改mocha的接口风格:

{
    // 修改mocha的接口风格,默认是bdd
    "ui": "bdd"
}
  • BDD提供了describe()it()before(), after(), beforeEach(), afterEach()方法
    • describe()定义一组测试套件
    • it()定义一个测试用例
    • before()钩子函数,在当前区块内第一个测试用例运行之前运行一次
    • after()钩子函数,在区块内最后一个测试用例运行之后运行一次
    • beforeEach()钩子函数,在当前区块内的每一个测试用例运行前运行一次
    • afterEach()钩子函数,在当前区块内的每一个测试用例运行后运行一次
const assert = require('assert');

describe('start a mocha test suit.', function() {
  before(function beforeHook() {
    console.log('should run before this suit.');
  });
  beforeEach(function beforeEachHook() {
    console.log('should run before each test.');
  });

  it('shuold return true.', function() {
    assert.equal(1, 1);
  });
  it('should return false.', function() {
    assert.equal(1, 2);
  });
});

通过上面的例子可以看到钩子的执行情况,如下图所示:

image

异步支持

  • 基于回调的异步支持

通过向 it() 回调函数添加一个参数(通常命名为done)的方式可以支持异步测试。done函数可以传入一个Error对象或Error子类对象或假值表示不通过测试用例,不传表示通过测试用例:

// 待测试的函数
function asyncFn(msg) {
  return new Promise((resolve, reject) => {
    setTimeout(reject, 1000, new Error(msg));
  })
}

// 测试用例
escribe('start a mocha test suit.', function() {
  it('async test with done', function(done) {
    asyncFn('a error')
      .then(() => {
        // 通过测试
        done();
      }).catch(err => {
        // 不通过测试
        done(err);
      });
});
  • 基于promise的异步支持

除了使用done支持异步用例,也可以直接返回一个promise对象,mocha会判断promise的状态来决定测试用例通过或不通过:

// 待测试的函数
function asyncFn(msg) {
  return new Promise((resolve, reject) => {
    setTimeout(reject, 1000, new Error(msg));
  })
}

// 返回promise对象支持异步测试用例
it('async test with promise.', function() {
    return asyncFn('a error');
});

注意,不能既传递done参数又返回promise对象,会出现错误。

  • 基于async/await的异步支持
it('async test with promise.', function() {
    return asyncFn('a error');
});

上述三种支持异步的方式的测试用例,运行后如下图所示:

image

  • 异步钩子

beforeafter等钩子也是既支持同步也支持异步,异步的写法和上述测试用例支持异步一样,如下图所示:

// mocha钩子既可以是同步的也可以是异步的
// 异步钩子则使用done函数
after(function(done) {
    setTimeout(() => {
      console.log('async after hook');
      done();
    }, 1000);
});
  • 补充,思考done异步的实现

如果测试的是同步代码,需要省略回调函数,而且mocha再执行完一个用例后会自动执行下一个测试用例。

那么思考一下,如何实现一段js执行一组包含同步或异步的任务,如果异步任务传递了参数done则等待异步完成,如果是同步则直接调用下一个异步任务。

function run(queue) {
  function next(index) {
    if (index < queue.length) {
      // 核心在于判断任务定义时是否定义了形参
      const fn = queue[index];
      // 如果没有形参定义(例如done),则运行完同步任务直接调用下一个任务
      if (fn.length === 0) {
        fn();
        next(++index);
      // 如果定义了形参,则等待用户手动调用done后开始下一个任务,依次支持异步
      } else {
        fn(function done() {
          console.log('done length', queue[index].length);
          next(++index);
        });
      }
    }
  }
  next(0);
}

测试下上述代码的结果,先测试一组同步任务:

const tasks = [
  function() {
    console.log(1);
  },
  function() {
    console.log(2);
  },
  function() {
    console.log(3);
  },
];

// 依次输出1,2,3
run(tasks);

const tasks2 = [
  function() {
    console.log(1);
  },
  function(done) {
    setTimeout(() => {
      console.log(2);
      done();
    }, 1000);
  },
  function() {
    console.log(3);
  },
];

// 先输出1,1s后输出2,紧接着输出3
run(tasks);

如果传入了done但是没有调用,结果是否符合预期呢?看下面的例子:

const tasks = [
  function() {
    console.log(1);
  },
  function(done) {
    setTimeout(() => {
      console.log(2);
    }, 1000);
  },
  function() {
    console.log(3);
  },
]

// 先输出1,1s后输出2,没有输出3
run(tasks);

可以看到结果是符合预期的,第二个异步任务没有调用done,也就没有再运行到下一个任务了。

测试执行时间

mocha的很多reporter会认为测试用例的执行周期时间(默认75ms)超过一定值的时候就是慢的,看下面的这个例子:

describe('slow test suit.', function() {
  it('should run 200ms.', function() {
    return asyncFn(200);
  });

  it('should run 500ms.', function() {
    return asyncFn(500);
  });
});

image

如上图所示,两个测试用例都超过了75ms,因此执行测试用例的时候红色的部分提示了是慢的。根据执行的时间,会将测试用例执行的效率氛围下面三种:

  • fast:低于设置时间的一半
  • normal:大于设置时间的一半到设置时间之间
  • slow:超过设置时间

如果有些测试用例就是比较耗时的话,我们可以自定义这个目标时间:

describe('slow test suit.', function() {
  // 设置slow时间是700ms,对当前套件内所有测试用例生效
  this.slow(700);

  it('should run 200ms.', function() {
    return asyncFn(200);
  });

  it('should run 500ms.', function() {
    return asyncFn(500);
  });
});

如下图所示,200ms的变成了fast500ms的变成了normal

image

当然了,也可以仅对单个测试用例生效:

it('should run 200ms.', function() {
    // 仅对当前测试用例生效
    this.slow(700);
    return asyncFn(200);
});

程序式使用