简体   繁体   English

与摩卡咖啡搭配使用

[英]Using mocha+chai together with co

When chai.expect assertions fail, they normally fail the test and the negative result gets added to the report for the test runner (in this case mocha ). chai.expect断言失败时,它们通常会在测试中失败,并且负面结果会添加到测试运行程序的报告中(在本例中为mocha )。

However, when I use a generator function wrapped using co.wrap() , as seen below, something strange happens: when the assertions pass, everything runs just fine. 但是,当我使用用co.wrap()包装的生成器函数时,如下所示,会发生一些奇怪的事情:当断言通过时,一切运行正常。 When the assertions fail, however, the test times out. 但是,当断言失败时,测试将超时。

How can co be used together with mocha + chai ? comocha + chai如何一起使用?


it('calls API and then verifies database contents', function(done) {
  var input = {
    id: 'foo',
    number: 123,
  };
  request
    .post('/foo')
    .send(input)
    .expect(201)
    .expect({
      id: input.id,
      number: input.number,
    })
    .end(function(err) {
      if (!!err) {
        return done(err);
      }

      // Now check that database contents are correct
      co.wrap(function *() {
        var dbFoo = yield foos.findOne({
          id: input.id,
        });
        continueTest(dbFoo);
      })();

      function continueTest(dbFoo) {
        //NOTE when these assertions fail, test times out
        expect(dbFoo).to.have.property('id').to.equal(input.id);
        expect(dbFoo).to.have.property('number').to.equal(input.number);
        done();
      }
    });
});

Solution: 解:

The problem arose due to co.wrap() swallowing the exception thrown by expect() , not allowing it bubble up to where it needed to for mocha to find it, as pointed out by @Bergi below. 问题出现了,由于co.wrap()吞咽通过抛出的异常expect()不允许它冒泡到它需要为mocha找到它,如下面@Bergi指出。

The solution was to use co() instead of co.wrap() , and add .catch() and pass that the done callback, as seen below. 解决方案是使用co()而不是co.wrap() ,并添加.catch()并传递done回调,如下所示。

  // Now check that database contents are correct co(function *() { var dbFoo = yield foos.findOne({ id: input.id, }); continueTest(dbFoo); }).catch(done); 

co.wrap catches exceptions from the generator, and rejects the returned promise. co.wrap从生成器中捕获异常,并拒绝返回的promise。 It "swallows" the error that is thrown from the assertions in continueTest . 它“吞噬”了由continueTest的断言引发的错误。 Btw, instead of using .wrap and immediately calling it, you can just call co(…) . 顺便说一句,您无需调用.wrap立即调用它,而只需调用co(…)

co(function*() {
    …
}).then(done, done); // fulfills with undefined or rejects with error

or 要么

co(function*() {
    …
    done();
}).catch(done);

Btw, to use co properly you'd put all your asynchronous functions in a single generator: 顺便说一句,要正确使用co,您需要将所有异步函数放在一个生成器中:

it('calls API and then verifies database contents', function(done) {
  co(function*() {
    var input = {
      id: 'foo',
      number: 123,
    };
    yield request
      .post('/foo')
      .send(input)
      .expect(201)
      .expect({
        id: input.id,
        number: input.number,
      })
      .endAsync(); // assuming you've promisified it

    // Now check that database contents are correct
    var dbFoo = yield foos.findOne({
      id: input.id,
    });

    expect(dbFoo).to.have.property('id').to.equal(input.id);
    expect(dbFoo).to.have.property('number').to.equal(input.number);

  }).then(done, done);
});

The root issue with your code here is that you're trying to yield in suptertest's end CPS callback; 此处代码的根本问题是,您试图在suptertest的end CPS回调中屈服。 since this function is not a generator function yield can not be used and your exceptions will disappear into the ether, as you've seen. 由于此函数不是生成器函数,因此无法使用yield并且您的异常将消失在以太图中,如您所见。

Using co.wrap directly is the proper way (when using co) to give mocha a promise that it can use to track the success or failure of a test that uses generator functions and yield for async flow control, you just need to serialize your test so that the db check runs after supertest. 直接使用co.wrap是正确的方法(当使用co时),使mocha可以保证它可以跟踪使用生成器功能和yield进行异步流控制的测试的成功或失败,您只需要序列化测试即可以便在超级测试后运行数据库检查。

Your solution solves this by using co to convert the generator function to a promise, and then uses that promise to "convert" back to mocha's CPS style async by using its catch function to call done if the db check throws: 您的解决方案通过使用co将生成器函数转换为promise来解决此问题,然后使用该promise通过db的catch函数调用done来将其“转换”回摩卡的CPS风格异步:

co(function *() {
    var dbFoo = yield foos.findOne({
      id: input.id,
    });
    continueTest(dbFoo);
}).catch(done);

Promises to the rescue 营救承诺

The good news is that supertest also supports promises and this can be done much more simply. 好消息是,超级测试也支持承诺,而且可以更简单地完成。

The important part (like shown in Bergi's answer) that you're missing is that promises, generator functions, and soon async/await, can work together. 您缺少的重要部分(如Bergi的答案所示)是Promise,生成器功能以及很快的异步/等待可以一起工作。 In this case we can take advantage of this to directly yield a promise, supertest's promise, inside the generator function. 在这种情况下,我们可以利用此优势在生成器函数内部直接产生一个Promise,即Supertest的Promise。

This keeps the db check directly in that test generator function where any exceptions will be handled properly by co.wrap and passed as a rejection to mocha. 这样可以使db直接在该测试生成器函数中进行检查,其中任何异常将由co.wrap正确处理并作为拒绝传递给mocha。

The test is now neatly serialized without any CPS detritus. 现在,该测试已整齐地序列化,而没有任何CPS碎屑。 And isn't that what the promise of these new async features in js are really about? 难道这不是js中这些新异步功能的承诺的真正意义吗?


it('calls API and then verifies database contents', co.wrap(function*() {
  var input = {
    id: 'foo',
    number: 123,
  };

  yield request
    .post('/foo')
    .send(input)
    .expect(201)
    .expect({
      id: input.id,
      number: input.number,
    });

  // Now check that database contents are correct
  var dbFoo = yield foos.findOne({
    id: input.id,
  });

  expect(dbFoo).to.have.property('id').to.equal(input.id);
  expect(dbFoo).to.have.property('number').to.equal(input.number);  
}));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM