Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sinon stub function used with destructuring

I wish to stub a function used in the file I'm currently testing. This function is required with a destructuring like this:

 const { theFunctionIWant } = require('path/to/module')

When testing, the stub is never called, and the real function proceed to be called. But when I require it 'normally' (i.e: without destructuring)

const myModule = require('path/to/module')

then the stub is correctly used and everything works fine

I sense that it's because of how the destructuring works and the fact that sinon stub the object property and not the function directly. Anyhow if you can provide me some insights I will be grateful !

like image 609
Sufiane Avatar asked Oct 01 '18 09:10

Sufiane


People also ask

What does Sinon stub return?

The sinon. stub() substitutes the real function and returns a stub object that you can configure using methods like callsFake() . Stubs also have a callCount property that tells you how many times the stub was called.

How do I stub a Sinon dependency?

To stub a dependency (imported module) of a module under test you have to import it explicitly in your test and stub the desired method. For the stubbing to work, the stubbed method cannot be destructured, neither in the module under test nor in the test.

What is Sinon used for?

Sinon JS is a popular JavaScript library that lets you replace complicated parts of your code that are hard to test for “placeholders,” so you can keep your unit tests fast and deterministic, as they should be.


1 Answers

The reason stubbing the method of the module doesn't work when using destructuring from a dependant module is quite simple and has to do with the time you bind the actual function reference. It doesn't have anything to do with CommonJS modules, Sinon or Node per se, so I'll start out with simple javascript examples.

const stub = (o, method) => (o[method] = () => "I am a stub");

const obj = {
  methodFoo() {
    return "I am foo";
  }
};

// same as doing `const methodFoo = obj.methodFoo;`
const { methodFoo } = obj; // "import" using destructuring

console.log("obj.methodFoo(): ", obj.methodFoo());
console.log("methodFoo()", methodFoo());

console.log("Stubbing out method!");
stub(obj, "methodFoo");

console.log("obj.methodFoo: ", obj.methodFoo());
console.log("methodFoo()", methodFoo());

If you run the example above, you will see that even though you have stubbed the methodFoo property of the obj "module", the directly bound reference still returns the old value!

This is because, when stubbing you are essentially assigning a new value (a function) to a property of an object (here: obj). New references to this new value (using obj.methodFoo) will print the new values, but if you have stored a reference to the old function you will still get the old return values when calling the old function.

The same applies to your original problem. If you in module A have a dependency on a function foo in module B and store that reference, then it doesn't matter if you assign a new value (for instance a stub) to the exported value, since you already stored a reference to the old value.

In essence:

This will be affected by stubbing

const A = require('./A');

function doFoo(){
    A.foo(); // will always evalute  A['foo']()
}

This will not be affected by stubbing

const myFoo = require('./A').foo;

function doFoo(){
    myFoo(); // will just evalute to the original value of A.foo()
}
like image 197
oligofren Avatar answered Sep 28 '22 03:09

oligofren