首页 文章

有没有办法让Chai使用异步Mocha测试?

提问于
浏览
79

我正在使用浏览器运行器在Mocha中运行一些异步测试,我正在尝试使用Chai的期望样式断言:

window.expect = chai.expect;
describe('my test', function() {
  it('should do something', function (done) {
    setTimeout(function () {
      expect(true).to.equal(false);
    }, 100);
  }
}

这不会给我正常的失败断言消息,而是我得到:

Error: the string "Uncaught AssertionError: expected true to equal false" was thrown, throw an Error :)
    at Runner.fail (http://localhost:8000/tests/integration/mocha/vendor/mocha.js:3475:11)
    at Runner.uncaught (http://localhost:8000/tests/integration/mocha/vendor/mocha.js:3748:8)
    at uncaught (http://localhost:8000/tests/integration/mocha/vendor/mocha.js:3778:10)

所以它显然正在捕捉错误,它只是没有正确显示它 . 任何想法如何做到这一点?我想我可以用一个错误对象调用“完成”,但后来我失去了像柴这样的东西的所有优雅,它变得非常笨重......

13 回答

  • 19

    根据@richardforrester http://staxmanade.com/2015/11/testing-asyncronous-code-with-mochajs-and-es7-async-await/提供的链接,如果省略done参数,describe可以使用返回的Promise .

    唯一的缺点就是那里有一个Promise,而不是任何异步函数(你可以用Promise包装它,你) . 但在这种情况下,代码可以大大减少 .

    它考虑了初始funcThatReturnsAPromise函数或期望中的失败:

    it('should test Promises', function () { // <= done removed
        return testee.funcThatReturnsAPromise({'name': 'value'}) // <= return added
            .then(response => expect(response).to.have.property('ok', 1));
    });
    
  • 1

    您的异步测试会在 it() 无法捕获的情况下生成异常,因为异常是在 it() 范围之外抛出的 .

    您在节点下使用 process.on('uncaughtException') 或在浏览器中使用 window.onerror() 捕获显示的捕获异常 .

    要解决此问题,您需要在 setTimeout() 调用的异步函数中捕获异常,以便将异常作为第一个参数调用 done() . 您还需要在没有参数的情况下调用 done() 以指示成功,否则mocha将报告超时错误,因为您的测试函数永远不会发出信号表示已完成:

    window.expect = chai.expect;
    
    describe( 'my test', function() {
      it( 'should do something', function ( done ) {
        // done() is provided by it() to indicate asynchronous completion
        // call done() with no parameter to indicate that it() is done() and successful
        // or with an error to indicate that it() failed
        setTimeout( function () {
          // Called from the event loop, not it()
          // So only the event loop could capture uncaught exceptions from here
          try {
            expect( true ).to.equal( false );
            done(); // success: call done with no parameter to indicate that it() is done()
          } catch( e ) {
            done( e ); // failure: call done with an error Object to indicate that it() failed
          }
        }, 100 );
        // returns immediately after setting timeout
        // so it() can no longer catch exception happening asynchronously
      }
    }
    

    在所有测试用例上这样做很烦人,而不是干,所以你可能想要为你提供一个功能 . 我们叫这个函数 check()

    function check( done, f ) {
      try {
        f();
        done();
      } catch( e ) {
        done( e );
      }
    }
    

    使用 check() ,您现在可以按如下方式重写异步测试:

    window.expect = chai.expect;
    
    describe( 'my test', function() {
      it( 'should do something', function( done ) {
        setTimeout( function () {
          check( done, function() {
            expect( true ).to.equal( false );
          } );
        }, 100 );
      }
    }
    
  • 1

    我解决了它将 try/catch 解压缩到一个函数 .

    function asyncExpect(test, done){
        try{
            test();
            done();
        } catch(error){
            done(error);
        }
    }
    

    然后在 it() 我打电话:

    it('shall update a host', function (done) {
                testee.insertHost({_id: 'host_id'})
                    .then(response => {
                        asyncExpect(() => {
                            expect(response).to.have.property('ok', 1);
                            expect(response).to.have.property('nModified', 1);
                        }, done);
                    });
    
            });
    

    它也可以调试 .

  • 0

    以下是我对ES6 / ES2015承诺和ES7 / ES2016 async / await的通过测试 . 希望这为研究此主题的任何人提供了一个很好的更新答案:

    import { expect } from 'chai'
    
    describe('Mocha', () => {
      it('works synchronously', () => {
        expect(true).to.equal(true)
      })
    
      it('works ansyncronously', done => {
        setTimeout(() => {
          expect(true).to.equal(true)
          done()
        }, 4)
      })
    
      it('throws errors synchronously', () => {
        return true
        throw new Error('it works')
      })
    
      it('throws errors ansyncronously', done => {
        setTimeout(() => {
          return done()
          done(new Error('it works'))
        }, 4)
      })
    
      it('uses promises', () => {
        var testPromise = new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve('Hello')
          }, 4)
        })
    
        testPromise.then(result => {
          expect(result).to.equal('Hello')
        }, reason => {
          throw new Error(reason)
        })
      })
    
      it('uses es7 async/await', async (done) => {
        const testPromise = new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve('Hello')
          }, 4)
        })
    
        try {
          const result = await testPromise
          expect(result).to.equal('Hello')
          done()
        } catch(err) {
          done(err)
        }
      })
    
      /*
      *  Higher-order function for use with async/await (last test)
      */
      const mochaAsync = fn => {
        return async (done) => {
          try {
            await fn()
            done()
          } catch (err) {
            done(err)
          }
        }
      }
    
      it('uses a higher order function wrap around async', mochaAsync(async () => {
        const testPromise = new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve('Hello')
          }, 4)
        })
    
        expect(await testPromise).to.equal('Hello')
      }))
    })
    
  • 0

    如果您喜欢承诺,请尝试Chai as Promised Q,允许这样的内容:

    doSomethingAsync().should.eventually.equal("foo").notify(done);
    
  • -2

    我在Mocha邮件列表中问了同样的事情 . 他们基本上告诉我这个:用Mocha和Chai编写异步测试:

    • 总是用 if (err) done(err); 开始测试

    • 始终以 done() 结束测试 .

    它解决了我的问题,并没有改变我的代码中间的一行(Chai期望的其他) . setTimout 不是进行异步测试的方法 .

    这是link to the discussion in the mailing list .

  • 1

    我发布了一个解决这个问题的软件包 .

    首先安装 check-chai 包:

    npm install --save check-chai

    然后在测试中,使用 chai.use(checkChai); ,然后使用 chai.check 帮助函数,如下所示:

    var chai = require('chai');
    var dirtyChai = require('dirty-chai');
    var checkChai = require('check-chai');
    var expect = chai.expect;
    chai.use(dirtyChai);
    chai.use(checkChai);
    
    describe('test', function() {
    
      it('should do something', function(done) {
    
        // imagine you have some API call here
        // and it returns (err, res, body)
        var err = null;
        var res = {};
        var body = {};
    
        chai.check(done, function() {
          expect(err).to.be.a('null');
          expect(res).to.be.an('object');
          expect(body).to.be.an('object');
        });
    
      });
    
    });
    

    Is there a way to get Chai working with asynchronous Mocha tests?我将其作为NPM包发布 .

    有关更多信息,请参阅https://github.com/niftylettuce/check-chai .

  • 93

    尝试chaiAsPromised!除了出色地命名,你可以使用如下语句:

    expect(asyncToResultingValue()).to.eventually.equal(true)

    Can confirm,适用于摩卡柴 .

    https://github.com/domenic/chai-as-promised

  • -2

    Jean Vincent's answer非常相关并受其启发,我们使用类似于他的 check 函数的辅助函数,但我们将其称为 eventually (这有助于它与chai-as-promised的命名约定相匹配) . 它返回一个函数,该函数接受任意数量的参数并将它们传递给原始回调 . 这有助于消除测试中额外的嵌套功能块,并允许您处理任何类型的异步回调 . 这是在ES2015中写的:

    function eventually(done, fn) {
      return (...args) => {
        try {
          fn(...args);
          done();
        } catch (err) {
          done(err);
        }
      };
    };
    

    用法示例:

    describe("my async test", function() {
      it("should fail", function(done) {
        setTimeout(eventually(done, (param1, param2) => {
          assert.equal(param1, "foo");   // this should pass
          assert.equal(param2, "bogus"); // this should fail
        }), 100, "foo", "bar");
      });
    });
    
  • 1

    我知道有很多重复的答案和建议的软件包来解决这个问题,但是我没有看到上面的简单解决方案为这两个用例提供了简洁的模式 . 我发布这个作为其他希望复制意大利面的人的综合答案:

    事件回调

    function expectEventCallback(done, fn) {
      return function() {
        try { fn(...arguments); }
        catch(error) { return done(error); }
        done();
      };
    }
    

    节点样式回调

    function expectNodeCallback(done, fn) {
      return function(err, ...args) {
        if (err) { return done(err); }
        try { fn(...args); }
        catch(error) { return done(error); }
        done();
      };
    }
    

    示例用法

    it('handles event callbacks', function(done) {
      something.on('event', expectEventCallback(done, (payload) => {
        expect(payload).to.have.propertry('foo');
      }));
    });
    
    it('handles node callbacks', function(done) {
      doSomething(expectNodeCallback(done, (payload) => {
        expect(payload).to.have.propertry('foo');
      }));
    });
    
  • 13

    测试期间的定时器和异步听起来非常粗糙 . 有一种方法可以通过基于承诺的方法来实现 .

    const sendFormResp = async (obj) => {
        const result = await web.chat.postMessage({
            text: 'Hello world!',
        });
       return result
    }
    

    此异步功能使用Web客户端(在本例中为Slacks SDK) . SDK负责API调用的异步性并返回有效负载 . 然后我们可以通过运行 expect 来测试chai中的有效负载异步promise中返回的对象 .

    describe("Slack Logic For Working Demo Environment", function (done) {
        it("Should return an object", () => {
            return sdkLogic.sendFormResp(testModels.workingModel).then(res => {
                expect(res).to.be.a("Object");
            })
        })
    });
    
  • 0

    什么对我来说非常好icm Mocha / Chai是来自Sinon图书馆的fakeTimer . 只需在必要时在测试中推进计时器 .

    var sinon = require('sinon');
    clock = sinon.useFakeTimers();
    // Do whatever. 
    clock.tick( 30000 ); // Advances the JS clock 30 seconds.
    

    有更快的完成测试的额外好处 .

  • 2

    您还可以使用域模块 . 例如:

    var domain = require('domain').create();
    
    domain.run(function()
    {
        // place you code here
    });
    
    domain.on('error',function(error){
        // do something with error or simply print it
    });
    

相关问题