【问题标题】:Testing promise chains vs async await [mocha/chai/sinon]测试承诺链与异步等待 [mocha/chai/sinon]
【发布时间】:2021-10-03 13:43:42
【问题描述】:

所以我是测试新手,并且已经设置了这个模拟数据库调用失败的方法的基本测试(对不起,如果我的术语不太正确)

我用的是sequelize,所以Job是模型,findAndCountAll是相关方法。

it('Should throw a 500 error if accessing the DB fails', () => {
    sinon.stub(Job, 'findAndCountAll');
    Job.findAndCountAll.throws();

    const req = {
        query: {
            index: 0,
            limit: 10,
            orderField: 'createdAt',
            order: 'DESC'
        }
    };

    adminController.getJobs(req, {}, () => {}).then(result => {
            expect(result).to.be.an('error');
            expect(result).to.have.property('statusCode', 500);

            done();
        })

    Job.findAndCountAll.restore();
})

我的问题是我的大部分代码都是使用承诺链编写的:

exports.getJobs = (req, res, next) => {
    const index = req.query.index || 0;
    const limit = req.query.limit || 10;
    const orderField = req.query.orderField || 'createdAt';
    const order = req.query.orderDirection || 'DESC';
 
    Job.findAndCountAll({
        // ...arguments
    })
    .then(results => {
        res.status(200).json({ jobs: results.rows, total: results.count });
        return // Attempted to add a return statement to enter the .then() block in the test
    })
    .catch(err => {
        if(!err.statusCode) err.statusCode = 500;

        next(err);
        return err; // Attempted to return the error to enter the .then() block in the test
    });

这不起作用,我的(不必要的)返回语句也无济于事。

但是,使用async await 重写方法确实有效(见下文)。但是我想避免重写我的所有代码,如果能理解这里的区别会很好。

我最好的猜测是,与其让 sinon 存根抛出错误,不如让它拒绝承诺?我只是不完全确定这是否正确,或者如何实现。我有点在不知道的文档中磕磕绊绊

任何帮助表示赞赏,

谢谢,

尼克

exports.getJobs = async(req, res, next) => {
    const index = req.query.index || 0;
    const limit = req.query.limit || 10;
    const orderField = req.query.orderField || 'createdAt';
    const order = req.query.orderDirection || 'DESC';

    try {
        const results = await Job.findAndCountAll({ //...params });

        // ... 

        res.status(200).json({ jobs: results.rows, total: results.count });
        return;
    } catch(err) {
        if(!err.statusCode) err.statusCode = 500;
        next(err);
        return err;
    } 
};

【问题讨论】:

  • 您的getJobs 函数根本没有return 语句。你需要让它返回承诺链Job.findAndCountAll({…}).then(…).catch(…)
  • 谢谢@Bergi,是的,我最终发现了那个(在下面的答案中)。我确实犯了很多错误,但学到了很多!

标签: javascript unit-testing mocha.js sinon


【解决方案1】:

所以我想我找到了答案:

sinon 中的 stub 需要返回一个被拒绝的 promise,而不是抛出一个错误:

sinon.stub(Job, 'findAndCountAll');
Job.findAndCountAll.rejects();

Afaik 这是因为您不能真正在异步代码中引发错误。

您正在测试的方法中的 Promise 链(在我的例子中是“getJobs”)需要返回该承诺。

所以不是

  Job.findAndCountAll({
            // ...arguments
        })
        .then(results => {
            res.status(200).json({...});
            return;
        })
        .catch(err => {
            if(!err.statusCode) err.statusCode = 500;

            next(err);
            return err;
        });

使用

   const results = Job.findAndCountAll({
            // ...arguments
        })
        .then(results => {
            res.status(200).json({...});
            return;
        })
        .catch(err => {
            if(!err.statusCode) err.statusCode = 500;

            next(err);
            return err;
        });
   return results;

此外,测试中的异步函数需要返回或等待,以便 mocha 知道等待。使用 done() 对我不起作用:

const result = await adminController.getJobs(req, {}, () => {});

expect(result).to.be.an('error');
expect(result).to.have.property('statusCode', 500);

Job.findAndCountAll.restore();

希望对某人有所帮助

**edit:如下所述,我忘记将done 作为参数传递,这就是该方法不起作用的原因

【讨论】:

  • Afaik this is because you can't really throw an error in async code. 可以,而且会被catch捕获
  • 你可以在异步代码中抛出一个错误,你只需要一个 try/catch 块围绕它。至于在代码中处理承诺,您没有将done 传递给it 的方法。 it('Should throw a 500 error if accessing the DB fails', (done) => { -- 其次,您可以通过将异步方法传递给it 来测试异步代码:it('Should throw a 500 error if accessing the DB fails', async () => {
  • 是的,刚刚注意到,ta。
猜你喜欢
  • 2018-02-13
  • 2018-10-17
  • 2016-04-29
  • 2018-03-06
  • 1970-01-01
  • 2016-09-22
  • 2018-09-03
  • 2017-05-21
  • 2020-02-28
相关资源
最近更新 更多