簡體   English   中英

如何使用Mocha + Chai編寫測試以期望setTimeout有異常?

[英]How to write a test using Mocha+Chai to expect an exception from setTimeout?

我有以下內容:

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);
});

但這並不能真正幫助我,因為測試行為已完全還原-當未引發case異常( setTimeout )時,它通過(應該失敗),而當引發異常時,測試失敗(應該成功)。

我在某處讀到有人提到全局錯誤處理程序,但如果可能的話,我想使用Mocha和/或Chai徹底解決此問題(我想Mocha已經在以某種方式使用它了)。

您不能從異步回調中處理異常,例如,請參見從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;
});

任何摩卡之類的語句beforeafter還是it會異步如果返回一個承諾工作。 我通常將以下內容用於異步測試。 如果您希望異步功能花費2秒鍾以上的時間,也不要忘記設置超時this.timeout(...)

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;
});

如果您測試的代碼使用拋出的回調調用setTimeout ,但沒有人捕獲到此異常,則:

1)此代碼已損壞

2)看到該問題的唯一方法是平台全局異常處理程序,例如用戶ComFreek提到的process.on('uncaughtException'

最后的機會是將setTimeout存根以進行測試(例如,使用sinon.stub )或僅手動進行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調用。 當心,有龍。

柴文件

如果未提供任何參數,則.throw會調用目標函數並斷言已引發錯誤。

所以你可以像

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM