Is there a way to get Chai working with asynchrono

2020-01-27 01:17发布

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

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...

13条回答
爷的心禁止访问
2楼-- · 2020-01-27 01:30

Very much related to and inspired by Jean Vincent's answer, we employ a helper function similar to his check function, but we call it eventually instead (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:

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");
  });
});
查看更多
爷的心禁止访问
3楼-- · 2020-01-27 01:30

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 expect against the object returned in the async 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");
        })
    })
});
查看更多
看我几分像从前
4楼-- · 2020-01-27 01:30

You can also use domain module. For example:

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
});
查看更多
倾城 Initia
5楼-- · 2020-01-27 01:34

I've published a package that resolves this issue.

First install the check-chai package:

npm install --save check-chai

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

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.

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

查看更多
我想做一个坏孩纸
6楼-- · 2020-01-27 01:37

I solved it extracting try/catch to a function.

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

Then in it() I call:

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.

查看更多
家丑人穷心不美
7楼-- · 2020-01-27 01:40

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');
  }));
});
查看更多
登录 后发表回答