【问题标题】:Unit testing with Supertest, Mocha & Sinon timing out使用 Supertest、Mocha 和 Sinon 进行单元测试超时
【发布时间】:2023-04-09 21:39:01
【问题描述】:

我正在尝试编写一个单元/集成测试,我想在其中获取数据库中的事物列表。不是它只是一个 GET,而是这些测试需要扩展到 POST、PUT 和 DELETE。

到目前为止,我的代码运行良好,我实际上可以从数据库中获取数据,但是一旦我尝试删除负责调用数据库的函数,Mocha 就会超时

1 次失败

1) /account_types GET 200 列表: 错误:超过 2000 毫秒的超时。确保在此测试中调用了 done() 回调。 在空。 (C:\Code\JS\general_admin_service\node_modules\mocha\lib\runnable.js:215:19)

我知道 done() 回调没有被调用,因为代码卡在某个地方,但是,我不明白我做错了什么。

我使用以下参考资料来了解我的位置:

我的代码如下:

测试:

'use strict';

var expect = require('chai').expect,
    request = require('supertest'),
    chance = require('chance').Chance(),
    server = require('../server'),
    sinon = require('sinon'),
    select = require('../../helpers/data_access/select');

describe("/account_types", function () {

    before(function(done){
        sinon
            .stub(select, "query_list")
            .returns([{id: "test"}]);

        done();
    });

    after(function(done){
        select
            .query_list
            .restore();

        done();
    });

    it('GET 200 List', function (done) {

        request(server.baseURL)
            .get('/api/v1/account_types')
            .set('Accept', 'application/json')
            .expect('Content-Type', 'application/json')
            .expect(200)
            .end(function (err, res) {
                /* istanbul ignore if */
                if (err)
                    return done(err);

                expect(res.body).to.include.keys('result');
                expect(res.body.result).to.not.be.null;
                expect(res.body.result).to.not.be.undefined;
                expect(res.body.result).to.be.an('Array');
                expect(res.body.result.length).to.be.above(0);

                //expect(select.query_list).to.have.been.calledOnce;

                return done();
            });
    });

});

重置端点:

var select = require('../helpers/data_access/select')

module.exports = function (server) {
        var query = "..."

        return select.query_list(res, next, db_config, query);
    });
};

select.js:

var sql = require('mssql');

module.exports = {
    query_list: function (res, next, config, sql_query) {
        return query(res, next, config, sql_query, true);
    },
    query_single: function (res, next, config, sql_query) {
        return query(res, next, config, sql_query, false);
    }
};

function query(res, next, config, sql_query, isList) {
    var connection = new sql.Connection(config);

    connection.connect(function (err) {
        if (err) {
            return on_error(err, res);
        }

        var request = new sql.Request(connection);

        request.query(sql_query, function (err, response) {
            connection.close();

            if (err) {
                return on_error(err, res);
            }

            if (isList) {
                return return_list(res, response, next);
            } else {
                return return_single(res, response, next);
            }
        });
    });
}

function on_error(error, res, next) {
    res.status(500).send(error);
    return next();
}

function return_list(res, response, next) {
    res.send({result: response});
    return next();
}

function return_single(res, response, next) {
    res.send({result: response[0]});
    return next();
}

我期望发生的事情是,因为我存根了 query_list 函数,如果我希望在我拥有的期望之后放置一个 console.log(res.body.result);,我应该会看到 [{id: "test"}] 的返回,但显然不是到了那个地步。

我做错了什么?

更新:添加了完整的 select.js 文件。

【问题讨论】:

  • 这可能取决于/api/v1/account_types 的处理程序是如何实现的(我想知道Restify 是否因为没有返回响应而卡住了)。
  • 据我所知,我没有做任何特别的事情。我基本上遵循了 resitfy 网站上的示例...
  • 从你的代码看起来,在select.query_list 的某个地方,响应是发送的(使用res.send() 或类似的东西)。由于您正在对该函数进行存根,因此永远不会发送响应,并且 Restify 会无限期地等待(直到 Mocha 超时)。
  • 好的,我明白你的意思了。我编辑了我的问题以包含完整的 select.js 文件。
  • 好的,所以我有点困惑。我明白你关于res.send() 的观点,但我是否需要将res.send() 存根,或者可能将res.send()next 从select.js 中的函数移出到端点?

标签: node.js mocha.js sinon


【解决方案1】:

正如您在 cmets 中已经阐明的那样,很难测试深度嵌套的代码。

使用回调或承诺通常会更好,这样您的应用程序的每个部分都将处理它负责的部分,但不会(太多)更多。因此,您的路由处理程序将处理请求响应。显然可以调用其他函数,例如执行数据库查询的函数,但不是让这些函数发回响应,而是使用“回调”到带有查询结果的路由处理程序的回调。

类似这样的:

server.get('/api/v1/account_types', function(req, res, next) {
  select.query_list(QUERY, function(err, records) {
    if (err) return next(err);
    res.send({ results : records });
    next();
  });
});

就使用Sinon 来测试这样的事情而言:这实际上取决于确切的实现。我可以提供一个快速示例,说明如何对select.query_list 的上述用法进行存根,以确保响应包含正确的数据。

基本存根如下所示:

sinon.stub(select, 'query_list').yieldsAsync(null, [ { id : 'test' } ]);

它的作用是,当select.query_list() 被调用时,它将调用它接收到的第一个回调参数(它通过检查每个参数来查看哪个是函数)和参数null, [ { id : 'test' } ]

这些是在处理程序中传递的回调函数的errrecords 参数。因此,您可以使用它来完全跳过数据库查询,并假装查询产生了特定的记录数组。

从那里,res.send() 被调用(这是您最初遇到的问题:它根本没有被调用,因为它是在您的应用程序的一部分中执行的,因为您的stub),如果结果响应数据符合预期,您可以检查您的测试。

如果您想在调用堆栈中更深地存根一个函数,它会变得有点复杂,但是使用正确的 Sinon 工具(如 .yields*,或使用间谍而不是存根)通常不是非常困难(前提是所有您要存根/监视的函数是可访问的,即导出的)。

【讨论】:

  • 优秀的答案!感谢您的帮助和知识分享:)
猜你喜欢
  • 2021-09-27
  • 2015-06-02
  • 2019-02-14
  • 1970-01-01
  • 2016-05-26
  • 1970-01-01
  • 2015-05-16
  • 2019-05-24
  • 2017-06-09
相关资源
最近更新 更多