简体   繁体   English

Sinon - 我可以暂时模拟存根上的方法吗?

[英]Sinon - Can I temporarily mock a method on a stub?

I'm retrofitting unit tests to legacy code, and have setup sinon and proxyquire to inject stubs for dependencies.我正在对遗留代码进行单元测试,并设置了 sinon 和 proxyquire 来为依赖项注入存根。

In some tests I need to verify that methods on one or more dependencies were called correctly, while allowing all other method calls on the same objects to behave like stubs (returning default values, rather than passing the calls to the real implementations.在某些测试中,我需要验证一个或多个依赖项上的方法是否被正确调用,同时允许对同一对象的所有其他方法调用表现得像存根(返回默认值,而不是将调用传递给真正的实现。

So far I've tried a number of approaches, and occasionally have gotten it to seemingly work until I do a little code cleanup, and things again break (when it works the code is in very poor shape--so while it seems like its working it's unclear if it will continue to work or what code is actually having the desired effect).到目前为止,我已经尝试了多种方法,偶尔让它看起来可以工作,直到我做了一些代码清理,然后事情再次中断(当它工作时,代码的形状很差 - 所以虽然它看起来像它工作目前尚不清楚它是否会继续工作或哪些代码实际上具有预期的效果)。

Below is what I'm currently trying, with comments about my intentions.以下是我目前正在尝试的内容,并对我的意图进行了评论。

const proxyquire = require('proxyquire')
const stubUtils = stub(require('./utils'))
const stubService = stub(require('./service'))

// Setup the SuT to default to talking to stubs
const systemUnderTest = proxyquire('./index', {
    './utils': stubUtils,
    './service': stubService
})

let sandbox
describe('index.doSomething()', () => {
    beforeEach(() => {
        // I'm attempting to revert any test-specific setup and put the dependencies back to default stubs
        sinon.reset();

        // the legacy code is configured by environment variables, between tests I want to reset process.env since each test requires a different configuration
        sandbox = sinon.createSandbox()
        sandbox.stub(process, 'env').value({})

        // someMethod() is printed in a bunch of logs which call .length on it, so my baseline setup needs to configure it
        stubService.someMethod.returns('')
    })

    afterEach(() => {
        // tests *should* call their own .verify(), but I'm assuming adding it here will catch it if the tests miss it
        sinon.verify()
        sandbox.restore()
    })

    // There are several basic "modes" for the code under test
    describe('Scenario A', () => {
        beforeEach('Scenario A Setup', () => {
            // Each scenario sets a few common settings
            process.env.SOME_CONFIG_VALUE = 'mode A'
            // ...
        })

        it('uses the environment variable appropriately', () => {
            // Here's where I'm struggling
            // In this one test I need to verify a call is made with the correct argument on an object that is otherwise behaving like a stub
            const expected = "the expected value"
            process.env.IMPORTANT_VALUE = expected

            // throws: stubUtils.restore is not a function
            //stubUtils.restore()

            // throws: TypeError: Attempted to wrap someMethod which is already wrapped
            const mockCall = sinon.mock(stubUtils).expects('someMethod').withArgs(expected)

            systemUnderTest.doSomething()

            mockCall.verify()
        })

        // it(...  // more tests, similarly needing to mock various methods on the stubs
    })

    // describe('Scenario B', () => {
    // ... and so on.  There are several scenarios, each with several unit tests

I figured it out.我想到了。 The root of the issue I was running into was that I was stub() 'ing an entire object and expecting to be able to .restore() it as well.我遇到的问题的根源是我正在stub() 'ing 整个 object 并期望能够.restore()它。

However, the .restore() method is still found on the object's methods and not the object itself.但是, .restore()方法仍然存在于对象的方法中,而不是 object 本身。

This is an example of my mistake:这是我的错误的一个例子:

const myObj = { doSomething: () => {} }
const stubObj = sinon.stub(myObj)
stubObj.restore()

And here it is fixed by moving the .restore() call to the function :在这里通过将.restore()调用移动到function来修复它:

const myObj = { doSomething: () => {} }
const stubObj = sinon.stub(myObj)
stubObj.doSomething.restore()

PS...附言...

I also found that I got the best test output by sticking with stubs.我还发现我通过使用存根得到了最好的测试 output。 Previously I thought I needed to use a mock() and .expects followed by a .verify() .以前我认为我需要使用mock().expects后跟.verify() That works, and it includes the details of what parameters were mismatched.这行得通,它包括哪些参数不匹配的详细信息。

However, I found the output of sinon.assert.calledWithMatch(stubObj.doSomething, expected) to be the easiest to read and spot exactly why it failed.但是,我发现sinon.assert.calledWithMatch(stubObj.doSomething, expected)的 output 是最容易阅读并准确找出失败原因的。

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

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