简体   繁体   English

在Node.js中使用Sinon.js模拟Postgres进行单元测试

[英]Mocking Postgres for unit tests with Sinon.js in Node.js

I am having trouble getting my head round how i can use sinon to mock a call to postgres which is required by the module i am testing, or if it is even possible. 我很难弄清楚如何使用sinon模拟对我正在测试的模块所要求的postgres的调用,或者甚至有可能。

I am not trying to test the postgres module itself, just my object to ensure it is working as expected, and that it is calling what it should be calling in this instance. 我不是在尝试测试postgres模块本身,而只是为了确保它按预期运行,并且没有在测试该实例中应该调用的对象。

I guess the issue is the require setup of node, in that my module requires the postgres module to hit the database, but in here I don't want to run an integration test I just want to make sure my code is working in isolation, and not really care what the database is doing, i will leave that to my integration tests. 我猜这是节点的必需设置,因为我的模块需要postgres模块才能访问数据库,但是在这里,我不想运行集成测试,我只是想确保我的代码可以独立工作,而不是真的在乎数据库在做什么,我将把它留给我的集成测试。

I have seen some people setting up their functions to have an optional parameter to send the mock/stub/fake to the function, test for its existence and if it is there use it over the required module, but that seems like a smell to me (i am new at node so maybe this isn't). 我见过有人将其函数设置为具有可选参数,以将模拟/存根/伪造函数发送给该函数,测试其存在性以及是否在所需的模块上使用它,但这对我来说似乎是一种气味(我是Node的新手,所以也许不是)。

I would prefer to mock this out, rather then try and hijack the require if that is possible. 我更愿意对此进行模拟,而不是尝试并劫持需求(如果可能的话)。

some code (please note this is not the real code as i am running with TDD and the function doesn't do anything really, the function names are real) 一些代码 (请注意,这不是真正的代码,因为我正在使用TDD运行,并且该函数实际上没有执行任何操作,函数名称是真实的)

TEST SETUP 测试设置

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

SAMPLE FUNCTION 样例功能

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
};

It might look a bit funny to wrap around the postgres module like this but there are some design as this app will have several "providers" and i want to expose the same API for them all so i can use them interchangeably. 像这样包装postgres模块可能看起来有点可笑,但是有一些设计,因为此应用程序将有几个“提供者”,我想为它们公开相同的API,以便可以互换使用。

UPDATE 更新

I decided that my test was too complicated, as i was looking to see if the connect call had been made AND then returning data, which smelt to me, so i stripped it back and put it into two tests: 我认为测试太复杂了,因为我想看看是否进行了连接调用,然后返回了对我来说很香的数据,因此我将其剥离并放入两个测试中:

The Mock Test 模拟测试

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();
    });

This works (i think) as without the postgres connector code it fails, with it passes (Boom :)) 这有效(我认为)是因为没有postgres连接器代码,它失败了,并且通过了(Boom :))

Issue now is with the second method, which i am going to use a stub (maybe a spy) which is passing 100% when it should fail, so i will pick that up in the morning. 现在的问题是第二种方法,我将使用一个存根(也许是间谍),它应该在失败时通过100%,所以我会在早上捡起来。

Update 2 更新2

I am not 100% happy with the test, mainly because I am not hijacking the client.query method which is the one that hits the database, but simply my execute method and forcing it down a path, but it allows me to see the result and assert against it to test behaviour, but would be open to any suggested improvements. 我对测试不是100%满意的,主要是因为我没有劫持击中数据库的client.query方法,而只是劫持了我的execute方法并将其强制沿着一条路径,但是它使我可以看到结果并断言它可以测试行为,但是可以接受任何建议的改进。

I am using a spy to catch the method and return null and a faux object with contains rows, like the method would pass back, this test will change as I add more Query behaviour but it gets me over my hurdle. 我正在使用间谍程序来捕获该方法并返回null和一个包含行的伪对象,就像该方法将回传一样,随着我添加更多查询行为,该测试将发生变化,但它使我步履维艰。

    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();
    });

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

It's about to be added to pg anyway and purportedly makes (mocking) unit-testing easier... from BrianC ( https://github.com/brianc/node-postgres/issues/1056#issuecomment-227325045 ): 无论如何,它将被添加到pg中,据称可以使(模拟)单元测试更加容易...来自BrianC( https://github.com/brianc/node-postgres/issues/1056#issuecomment-227325045 ):

Checkout https://github.com/brianc/node-pg-pool - it's going to be the pool implementation in node-postgres very soon and doesn't rely on singletons which makes mocking much easier. 检出https://github.com/brianc/node-pg-pool-很快将在node-postgres中实现池实现,并且不依赖单例,这使得模拟变得更加容易。 Hopefully that helps! 希望有帮助!

I very explicitly replace my dependencies. 我非常明确地替换了我的依赖项。 It's probably not the best solution but all the other solutions I saw weren't that great either. 这可能不是最好的解决方案,但我看到的所有其他解决方案也都不太好。

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

You add this code to the module under test. 您将此代码添加到被测模块。 In my tests I call the inject method and replace the real object. 在测试中,我调用了inject方法并替换了真实对象。 The reason why I don't 100% like it is because you have to add extra code only for testing. 我不100%喜欢它的原因是因为您只需要添加额外的代码即可进行测试。

The other solution is to read the module file as a string and use vm to manually load the file. 另一种解决方案是将模块文件读取为字符串,然后使用vm手动加载该文件。 When I investigated this I found it a little to complex so I went with just using the inject function. 当我调查此问题时,发现它有些复杂,因此我只使用了inject函数。 It's probably worth investigating this approach though. 不过,可能值得研究这种方法。 You can find more information here . 您可以在此处找到更多信息。

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

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