使用 Promise 实现的单元测试框架

发表于:2019-5-06 11:19

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:gitai.me    来源:gitai.me

  写完才发现,到底是单元测试框架还是单元测试函数,是个问题?毕竟才不到 50 行。这里来理理如何用 Promise 实现单元测试。
   test('A passing test', (assert) => {
  assert.pass('This test will pass.');
  assert.end();
  });
  例子是这样的,有那么一个 test 方法,接受一个 Label 和函数作为参数。
  并且有如下约束,end 必须执行,且前面的所有断言均为真,那就需要一个变量来储存这个状态。
   function test(label, fn) {
  let handle = (err, msg) => {
  console.log(label);
  if (err) {
  console.log(`[err]`, err);
  return;
  }
  console.log(`[ok] ${msg}`);
  };
  return new Promise((resolve, reject) => {
  let ok = false;
  let data = null;
  let assert = {
  pass: (msg) => (ok = true, data=msg),
  throw: (err) => (ok = false, data=err),
  end () {
  ok ? resolve (data) : reject (data);
  }
  };
  fn (assert);
  ok = false;
  assert.end();
  })
  .then((msg) => handle(null, msg), (err) => handle(err))
  }
  test('A passing test', (assert) => {
  assert.pass('This test will pass.');
  assert.end();
  });
  test('Throw a error', (assert) => {
  throw new Error('Bomb.');
  assert.end();
  });
  运行一下,会获得如下结果
   $ node ./src/test.js
  A passing test
  [ok] This test will pass.
  Throw a error
  [err] Error: Bomb.
  at test (C:\Users\Administrator\Desktop\transer\src\test.js:35:9)
  at Promise (C:\Users\Administrator\Desktop\transer\src\test.js:21:5)
  at new Promise (<anonymous>)
  at test (C:\Users\Administrator\Desktop\transer\src\test.js:11:10)
  at Object.<anonymous> (C:\Users\Administrator\Desktop\transer\src\test.js:34:1)
  at Module._compile (internal/modules/cjs/loader.js:701:30)
  at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10)
  at Module.load (internal/modules/cjs/loader.js:600:32)
  at tryModuleLoad (internal/modules/cjs/loader.js:539:12)
  at Function.Module._load (internal/modules/cjs/loader.js:531:3)
   完美,但是比起 tape 似乎少了什么,来对比一下。
   $ node ./src/test.js
  TAP version 13
  # A passing test
  ok 1 This test will pass.
  # Throw a error
  C:\Users\Administrator\Desktop\transer\src\test.js:8
  throw new Error('Bomb.');
  ^
  Error: Bomb.
  at Test.test (C:\Users\Administrator\Desktop\transer\src\test.js:8:9)
  at Test.bound [as _cb] (C:\Users\Administrator\Desktop\transer\node_modules\tape\lib\test.js:77:32)
  at Test.run
  哦,他居然不能捕获 throw,那我们换个例子。
   test('Throw a error', (assert) => {
  assert.fail('Bomb');
  assert.end();
  });
  
   $ node ./src/test.js
  TAP version 13
  # A passing test
  ok 1 This test will pass.
  # Throw a error
  not ok 2 Bomb
  ---
  operator: fail
  at: Test.test (C:\Users\Administrator\Desktop\transer\src\test.js:8:10)
  stack: |-
  Error: Bomb
  at Test.assert [as _assert] (C:\Users\Administrator\Desktop\transer\node_modules\tape\lib\test.js:226:54)
  at Test.bound [as _assert] (C:\Users\Administrator\Desktop\transer\node_modules\tape\lib\test.js:77:32)
  at Test.fail
  ...
  1..2
  # tests 2
  # pass  1
  # fail  1
   相比他,我们的实在是太完美了,就差个报告。
  首先是最前面的 TAP version 13 我们要写个方法,让他只出现一次。必须写在一个全局唯一的实例里面,还不能污染其他环境。
  JavaScript 的左查询(赋值操作)有个特性,会先再作用域里面查找,然后替换,常用于惰性求值。于是有了如下代码,用个子函数作为执行体,外部通过闭包保存全局数据。
   function test (label, fn) {
  console.log('TinyTest version 0.1');
  function rawTest (label, fn) {
  let handle = (err, msg) => {
  // ...
  };
  new Promise((resolve, reject) => {
  // ...
  })
  .then((msg) => handle(null, msg), (err) => handle(err));
  }
  test = (label, fn) => rawTest(label, fn);
  test(label, fn);
  }
  运行到结束时,会用箭头表达式,生成匿名函数覆盖上面定义的 test。
  这样就能保证 'TinyTest version 0.1' 只会输出一遍。
  接下来,既然有了闭包,我们可以在里面存全局变量,那么 id 就可以放进去。
   function test (label, fn) {
  let id = 0;
  console.log('TinyTest version 0.2')
  function rawTest (label, fn, id) {
  let handle = (err, msg) => {
  console.log(id, label);
  // ...
  };
  new Promise((resolve, reject) => {
  // ...
  })
  .then((msg) => handle(null, msg), (err) => handle(err));
  }
  test = (label, fn) => rawTest(label, fn, ++id);
  test(label, fn);
  }
  这样就可以得到自增 id,输出如下结果。
   $ node ./src/test.js
  TinyTest version 0.1
  1 'A passing test'
  [ok] This test will pass.
  2 'Throw a error'
  [err] Error: Bomb.
  at test (C:\Users\Administrator\Desktop\transer\src\test.js:41:9)
  at Promise (C:\Users\Administrator\Desktop\transer\src\test.js:24:7)
  最后就是那个成功失败数量的报告了,通过变量覆盖确定第一次执行容易,但是确定最后一个执行就难了。
  这里可以用 Node.js 生命周期里面的,exit 事件。
  最终实现如下:
   function test (label, fn) {
  let id = 0;
  let success = 0;
  process.on('exit', () => {
  console.log(`# pass ${success}/${id}`);
  console.log(`# fail ${id - success}/${id}`);
  });
  console.log('TinyTest version 0.2')
  function rawTest (label, fn, id) {
  let handle = (err, msg) => {
  console.log(id, label);
  if (err) {
  console.log(`[err]`, err);
  return;
  }
  success++;
  console.log(`[ok] ${msg}`);
  };
  new Promise((resolve, reject) => {
  let ok = false;
  let data = null;
  let assert = {
  pass: (msg) => (ok = true, data=msg),
  throw: (err) => (ok = false, data=err),
  end () {
  ok ? resolve (data) : reject (data);
  }
  };
  fn (assert);
  ok = false;
  assert.end();
  })
  .then((msg) => handle(null, msg), (err) => handle(err));
  }
  test = (label, fn) => rawTest(label, fn, ++id);
  test(label, fn);
  }
  测试如下:
   $ node ./src/test.js
  TinyTest version 0.2
  1 'A passing test'
  [ok] This test will pass.
  2 'Throw a error'
  [err] Error: Bomb.
  at test (C:\Users\Administrator\Desktop\transer\src\test.js:47:9)
  at Promise (C:\Users\Administrator\Desktop\transer\src\test.js:30:7)
  at new Promise (<anonymous>)
  at rawTest (C:\Users\Administrator\Desktop\transer\src\test.js:20:5)
  at test (C:\Users\Administrator\Desktop\transer\src\test.js:37:25)
  at Object.<anonymous> (C:\Users\Administrator\Desktop\transer\src\test.js:46:1)
  at Module._compile (internal/modules/cjs/loader.js:701:30)
  at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10)
  at Module.load (internal/modules/cjs/loader.js:600:32)
  at tryModuleLoad (internal/modules/cjs/loader.js:539:12)
  # pass 1/2
  # fail 1/2
  完美收工。

     上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号