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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11235815/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 05:04:07  来源:igfitidea点击:

Is there a way to get Chai working with asynchronous Mocha tests?

javascriptunit-testingmocha

提问by Thomas Parslow

I'm running some asynchronous tests in Mocha using the Browser Runner and I'm trying to use Chai's expect style assertions:

我正在使用 Browser Runner 在 Mocha 中运行一些异步测试,并且我正在尝试使用 Chai 的 expect 样式断言:

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

This doesn't give me the normal failed assertion message, instead I get:

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

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)

So it's obviously catching the error, it's just not displaying it correctly. Any ideas how to do this? I guess I could just call "done" with an error object but then I lose all the elegance of something like Chai and it becomes very clunky...

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

回答by Jean Vincent

Your asynchronous test generates an exception, on failed expect()ations, that cannot be captured by it()because the exception is thrown outside of it()'s scope.

您的异步测试会在失败时生成一个异常,该异常expect()无法被捕获,it()因为异常是在it()的范围之外抛出的。

The captured exception that you see displayed is captured using process.on('uncaughtException')under node or using window.onerror()in the browser.

您看到的捕获异常是使用process.on('uncaughtException')under node 或 usingwindow.onerror()在浏览器中捕获的。

To fix this issue, you need to capture the exception within the asynchronous function called by setTimeout()in order to call done()with the exception as the first parameter. You also need to call done()with no parameter to indicate success, otherwise mocha would report a timeout error because your test function would never have signaled that it was done:

要解决此问题,您需要在调用的异步函数中捕获异常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
  }
}

Doing so on all your test cases is annoying and not DRY so you might want to provide a function to do this for you. Let's call this function check():

在所有测试用例上这样做很烦人,而不是 DRY,因此您可能希望提供一个函数来为您执行此操作。让我们调用这个函数check()

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

With check()you can now rewrite your asynchronous tests as follows:

现在,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 );
  }
}

回答by RichardForrester

Here are my passing tests for ES6/ES2015 promises and ES7/ES2016 async/await. Hope this provides a nice updated answer for anyone researching this topic:

这是我对 ES6/ES2015 promises 和 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')
  }))
})

回答by xinthink

If you like promised, try Chai as Promised+ Q, which allow something like this:

如果你喜欢 promise,试试Chai as Promised+ Q,它允许这样的事情:

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

回答by DjebbZ

I asked the same thing in the Mocha mailing list. They basically told me this : to write asynchronous test with Mocha and Chai :

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

  • always start the test with if (err) done(err);
  • always end the test with done().
  • 总是开始测试 if (err) done(err);
  • 总是以 结束测试done()

It solved my problem, and didn't change a single line of my code in-between (Chai expectations amongst other). The setTimoutis not the way to do async tests.

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

Here's the link to the discussion in the mailing list.

这是邮件列表中讨论链接

回答by Manil

Try chaiAsPromised! Aside from being excellently named, you can use statements like:

尝试chaiAsPromised!除了出色的命名之外,您还可以使用以下语句:

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

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

Can confirm, works very well for Mocha + Chai.

可以确认,适用于 Mocha + Chai。

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

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

回答by Ryan McGeary

Very much related to and inspired by Jean Vincent's answer, we employ a helper function similar to his checkfunction, but we call it eventuallyinstead (this helps it match up with the naming conventions of chai-as-promised). It returns a function that takes any number of arguments and passes them to the original callback. This helps eliminate an extra nested function block in your tests and allows you to handle any type of async callback. Here it is written in ES2015:

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

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

Example Usage:

示例用法:

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

回答by Sukima

I know there are many repeat answers and suggested packages to solve this however I haven't seen the simple solutions above offer a concise pattern for the two use cases. I am posting this as a consolidated answer for other who wish to copy-pasta:

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

event callbacks

事件回调

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

node style callbacks

节点样式回调

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

example usage

示例用法

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

回答by niftylettuce

I've published a package that resolves this issue.

我发布了一个解决这个问题的包。

First install the check-chaipackage:

首先安装check-chai软件包:

npm install --save check-chai

npm install --save check-chai

Then in your tests, use chai.use(checkChai);and then use the chai.checkhelper function as shown below:

然后在您的测试中,使用chai.use(checkChai);然后使用chai.checkhelper 函数,如下所示:

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

  });

});

Per Is there a way to get Chai working with asynchronous Mocha tests?I published this as an NPM package.

Per有没有办法让 Chai 使用异步 Mocha 测试?我将其发布为 NPM 包。

Please see https://github.com/niftylettuce/check-chaifor more information.

请参阅https://github.com/niftylettuce/check-chai了解更多信息。

回答by Justin Rice

Timers during tests and async sounds pretty rough. There is a way to do this with a promise based approach.

测试和异步期间的计时器听起来很粗糙。有一种方法可以使用基于承诺的方法来做到这一点。

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

This async function uses a Web client (in this case it is Slacks SDK). The SDK takes care of the asynchronous nature of the API call and returns a payload. We can then test the payload within chai by running expectagainst the object returned in the async promise.

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

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

回答by Amio.io

I solved it extracting try/catchto a function.

我解决了它提取try/catch到一个函数。

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

Then in it()I call:

然后在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);
                });

        });

It's also debugable.

它也是可调试的。