如何使用Mocha + Chai编写测试以期望setTimeout出现异常?

时间:2018-03-18 07:48:53

标签: javascript typescript mocha chai

我有以下内容:

it('invalid use', () => {
  Matcher(1).case(1, () => {});
});

case方法应该在延迟一段时间之后抛出,我怎么能为Mocha / Chai描述它是我想要的 - 测试应该通过(并且在不抛出异常时不能通过)?

考虑case方法限制,不能更改。

出于测试目的,它应该等同于:

it('setTimeout throw', _ => {
  setTimeout(() => { throw new Error(); }, 1); // this is given, cannot be modified
});

我试过了:

it('invalid use', done => {
  Matcher(1).case(1, () => {});
  // calls done callback after 'case' may throw
  setTimeout(() => done(), MatcherConfig.execCheckTimeout + 10);
});

但这并没有真正帮助我,因为测试行为已经完全恢复 - 当没有抛出casesetTimeout)的异常时,它会通过(应该失败)并抛出异常测试失败(应该成功)。

我读过有人提到全局错误处理程序的地方,但我想用Mocha和/或Chai干净地解决这个问题,如果可能的话(我猜Mocha已经在某种程度上使用它了)。

4 个答案:

答案 0 :(得分:1)

您无法处理异步回调中的异常,例如见Handle error from setTimeout。这与ECMAScript使用的执行模型有关。我认为捕获它的唯一方法实际上是采用一些特定于环境的全局错误处理,例如: Node.js中的process.on('uncaughtException', ...)

但是,如果将函数转换为Promises,则可以使用Chai插件chai-as-promsied轻松测试它:

import * as chai from 'chai';

import chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
const expect = chai.expect;

it('invalid use', async () => {
  await expect(Matcher(1).case(1, () => {})).to.eventually.be.rejected;
});

答案 1 :(得分:0)

来自Chai documentation

  

当没有提供参数时,.throw调用目标函数并断言抛出错误。

所以你可以像

那样
expect(Matcher(1).case(1, () => {})).to.throw

答案 2 :(得分:0)

如果您返回承诺,任何Mocha语句(如afteritthis.timeout(...)都将异步工作。我通常使用类似下面的东西进行异步测试。 如果您希望异步函数花费超过2秒,也不要忘记设置超时it('some test', () => { return new Promise(function(resolve,reject){ SomeAsyncFunction(function(error,vals) { if(error) { return reject(error); } else { try { //do some chai tests here } catch(e) { return reject(e); } return resolve(); } }); }); });

.case

特别针对您的情况,因为我们期望在一段时间后抛出一些错误(假设您提供给it('invalid use', () => { //define the promise to run the async function let prom = new Promise(function(resolve,reject){ //reject the promise if the function does not throw an error //note I am assuming that the callback won't run if the error is thrown //also note this error will be passed to prom.catch so need to do some test to make sure it's not the error you are looking for. Matcher(1).case(1, () => {return reject(new Error('did not throw'))}); }); prom.catch(function(err){ try { expect(err).to.be.an('error'); expect(err.message).to.not.equal('did not throw'); //more checks to see if err is the error you are looking for } catch(e) { //err was not the error you were looking for return Promise.reject(e); } //tests passed return Promise.resolve(); }); //since it() receives a promise as a return value it will pass or fail the test based on the promise. return prom; }); 的空回调由于抛出异常而不应该运行)然后您可以编写它类似的东西:

data=[ [2, ["00_01_02"]],
  [1, ["00_03_04"]],
  [3, ["00_03_04"]],
  [6, ["00_03_04"]],
  [4, ["01_02_03"]],
  [5, ["01_02_03"]],
 ]

import itertools

print([list(j) for i,j in itertools.groupby(data,key=lambda x:x[1][0])])

答案 3 :(得分:0)

如果您的测试代码使用一个抛出的回调调用setTimeout并且没有人捕获,那么这是例外:

1)此代码已损坏

2)查看该问题的唯一方法是用户ComFreek提到的process.on('uncaughtException'等平台全局异常处理程序

最后的机会是在测试期间(例如使用setTimeout)或仅手动存根sinon.stub

在这样的存根setTimeout中,您可以修饰超时处理程序,检测异常并调用适当的断言。

注意,这是最后的解决方案 - 您的应用程序代码已损坏,应该修复以正确传播错误,不仅用于测试,而且......好吧,做好代码。

伪代码示例:



it('test', (done) => {

    const originalSetTimeout = setTimeout;
    setTimeout = (callback, timeout) => {
        originalSetTimeout(() => {
            try {
                callback();
            } catch(error) {
                 // CONGRATS, you've intercepted exception
                 // in _SOME_ setTimeout handler
            }
        }, timeout)
    }
    yourTestCodeThatTriggersErrorInSomeSetTimeoutCallback(done);
})




注意2:我故意没有写过正确的异步清理代码,这是一个功课。再次,请参阅sinon.js及其sandbox

注3:它将在测试期间捕获所有setTimeout个电话。当心,有龙。