【问题标题】:Mocking Postgres for unit tests with Sinon.js in Node.js在 Node.js 中使用 Sinon.js 模拟 Postgres 进行单元测试
【发布时间】:2012-10-27 17:58:38
【问题描述】:

我无法弄清楚如何使用 sinon 来模拟对我正在测试的模块所要求的 postgres 的调用,或者是否有可能。

我不是在尝试测试 postgres 模块本身,只是我的对象以确保它按预期工作,并且它正在调用它在这种情况下应该调用的东西。

我想问题是节点的要求设置,因为我的模块需要 postgres 模块才能访问数据库,但是在这里我不想运行集成测试,我只想确保我的代码正常工作孤立地,并不真正关心数据库在做什么,我将把它留给我的集成测试。

我看到有些人设置他们的函数有一个可选参数来发送模拟/存根/假给函数,测试它的存在,如果它在那里,在所需的模块上使用它,但这似乎是一个对我来说闻起来(我是节点的新手,所以也许这不是)。

如果可能的话,我更愿意模拟这个,而不是尝试劫持 require。

一些代码(请注意,这不是真正的代码,因为我正在使用 TDD 和 函数实际上并没有做任何事情,函数名称是真实的)

测试设置

describe('#execute', function () {
it('should return data rows when executing a select', function(){
 //Not sure what to do here
});
});

示例函数

PostgresqlProvider.prototype.execute = function (query, cb) {
var self = this;

if (self.connection === "")
    cb(new Error('Connection can not be empty, set Connection using Init function'));

if (query === null)
    cb(new Error('Invalid Query Object - Query Object is Null'))

if (!query.buildCommand)
    cb(new Error("Invalid Query Object"));

//Valid connection and query
};

像这样包裹 postgres 模块可能看起来有点有趣,但有一些设计,因为这个应用程序将有几个“提供者”,我想为它们都公开相同的 API,以便我可以互换使用它们。

更新

我认为我的测试太复杂了,因为我想看看是否已经进行了连接调用,然后返回数据,这对我来说很重要,所以我将其剥离并放入两个测试中:

模拟测试

it('should call pg.connect when a valid Query object is parsed', function(){
        var mockPg = sinon.mock(pg);
        mockPg.expects('connect').once;            

        Provider.init('ConnectionString');
        Provider.execute(stubQueryWithBuildFunc, null, mockPg);

        mockPg.verify();
    });

这有效(我认为)因为没有 postgres 连接器代码它会失败,但它会通过(Boom :))

现在的问题是第二种方法,我将使用一个存根(可能是一个间谍),当它应该失败时,它会通过 100%,所以我会在早上拿起它。

更新 2

我对测试不是 100% 满意,主要是因为我没有劫持 client.query 方法,它是命中数据库的方法,而只是我的 execute 方法并强制它走上一条路径,但它允许我查看结果并对其进行断言以测试行为,但愿意接受任何建议的改进。

我正在使用 spy 来捕获该方法并返回 null 和一个包含行的虚假对象,就像该方法会传回一样,这个测试会随着我添加更多 Query 行为而改变,但它让我克服了障碍。

    it('should return data rows when a valid Query object is parsed', function(){

        var fauxRows = [
            {'id': 1000, 'name':'Some Company A'},
            {'id': 1001, 'name':'Some Company B'}
        ];

       var stubPg = sinon.stub(Provider, 'execute').callsArgWith(1, null, fauxRows);

       Provider.init('ConnectionString');
       Provider.execute(stubQueryWithBuildFunc, function(err, rows){
           rows.should.have.length(2);
       }, stubPg);

       stubPg.called.should.equal.true;
       stubPg.restore();
    });

【问题讨论】:

  • 似乎首先需要一个有效的连接——这意味着一个正在运行的 postgres 实例来响应连接以允许单元测试......对吗?
  • 老实说,我们改变了 tract 并且不再使用 postgres,所以这可能有点不对劲,但如果你得到了正确的模拟,你不应该需要一个正在运行的实例,存根/间谍应该行动作为连接,您应该能够在此基础上测试您的代码。
  • 我已经与主要贡献者 brinc 取得了联系,他提到 pg 即将发生一些重大的变化,这将使单元测试更加容易......我会详细说明为将来的访问者回答这个问题...
  • @Reinsbrain 真棒

标签: javascript node.js sinon


【解决方案1】:

使用 pg-pool:https://www.npmjs.com/package/pg-pool

无论如何它都将被添加到 pg 中,据称它使(模拟)单元测试更容易......来自 BrianC(https://github.com/brianc/node-postgres/issues/1056#issuecomment-227325045):

Checkout https://github.com/brianc/node-pg-pool - 它将很快成为 node-postgres 中的池实现,并且不依赖单例,这使得模拟变得更加容易。希望对您有所帮助!

【讨论】:

    【解决方案2】:

    我非常明确地替换了我的依赖项。这可能不是最好的解决方案,但我看到的所有其他解决方案也不是那么好。

    inject: function (_mock) {
      if (_mock) { real = _mock; }
    }
    

    您将此代码添加到被测模块中。在我的测试中,我调用了注入方法并替换了真实对象。我不是 100% 喜欢它的原因是您必须添加额外的代码仅用于测试。

    另一种解决方案是将模块文件作为字符串读取并使用vm手动加载文件。当我对此进行调查时,我发现它有点复杂,所以我只使用了注入功能。不过,可能值得研究这种方法。您可以找到更多信息here

    【讨论】:

    • 感谢您的回复,有趣的方法,但如果可以的话,我会尽量保持我的模块干净,我找到了一个我已经更新问题的解决方法,导致我遇到另一个问题用第二个测试头脑。
    猜你喜欢
    • 2018-06-07
    • 2013-03-25
    • 2012-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-17
    • 2023-03-29
    • 1970-01-01
    相关资源
    最近更新 更多