【问题标题】:silent errors in mocha tests with generator functions使用生成器功能的 mocha 测试中的静默错误
【发布时间】:2025-12-17 08:15:01
【问题描述】:

我想对一个函数进行单元测试。 在该函数中,我将 Co 与生成器函数一起使用。 当发生错误时,我会捕获它,并使用错误调用 cb

在我的单元测试中,我做了一个错误的断言,但 mocha 没有报告它,它只是超时了:

//function to test

function start(data, cb) {
  co(function * coStart() {
    yield Promise.reject('err'); // yield error for sake of demo
  }).then(function(result){
    return cb(null, result);
  }).catch(function (err) {
    // we get here
    return cb(err);
  });
}

// mocha test

it('fails on incorrect data', function(done){
  MyModel.start({'foo': 'bar'}, function(err, res){
    assert.equal(err, 'err2'); //this fails but mocha just stops here and times out
    done();
  });
});

显然我在这里做错了什么?

我知道你可以向 mocha 返回一个承诺并在测试中省略完成回调,但是我的函数“开始”不能返回一个承诺,它就像中间件,所以它应该与回调一起工作

【问题讨论】:

    标签: javascript mocha.js co


    【解决方案1】:

    您的代码执行与此类似的操作:

    Promise.reject('err')
           .catch(() => {
             // throw another exception
             throw 'foo';
           });
    

    即:在.catch() 子句中,同步抛出另一个异常(在您的情况下,assert.equal(err, 'err2') 抛出的异常),未处理(例如,另一个.catch() 子句)。这将导致第二个异常被忽略(请参阅this answer 以获得解释),但它会阻止测试用例完成(永远不会到达done() 行,因此会使测试用例超时)。

    如果您确实需要回调支持,您可以通过在 start() 中添加另一个 .catch() 子句或异步调用回调来解决此问题:

    return setImmediate(function() { cb(null, result) });
    ...
    return setImmediate(function() { cb(err) });
    ...
    

    但理想情况下,您应该考虑完全删除回调支持并仅传递承诺的可能性。 Mocha 支持开箱即用的 Promise,因此代码如下所示:

    function start(data) {
      return co(function * coStart() {
        yield Promise.reject('err');
      });
    }
    
    it('fails on incorrect data', function(){
      return start({'foo': 'bar'}).then(function() {
        throw new Error('should not be reached');
      }, function(err) {
        assert.equal(err, 'err2');
      });
    });
    

    【讨论】:

    • 谢谢伙计,我终于明白了! setImmediate 有效。使用它有什么影响吗,因为感觉有点像破解“正常流程”
    • 您可以使用dezalgo 之类的替代方案,这样可以提供更简洁的代码。我自己更喜欢“完全承诺”的解决方案,但我意识到这并不总是一种选择:D
    • 谢谢!我去看看