I'm having an issue with stubbing a particular function with sinon after having used proxyquire.
Example:
// a.js
const api = require('api');
module.exports = (function () {
return {
run,
doStuff
};
function run() {
return api()
.then((data) => {
return doStuff(data);
})
}
function doStuff(data) {
return `Got data: ${data}`;
}
})()
// a.spec.js - in the test
a = proxyquire('./a', {
'api': () => Promise.resolve('data')
})
sinon.stub(a, 'doStuff');
// RUN TEST - call a.run()
I know it isn't working because it calls the original doStuff instead of a mocked/stubbed doStuff.
I know it isn't working because it calls the original doStuff instead
of a mocked/stubbed doStuff.
That is because function run() in a.js invokes function doStuff(data) inside the closure function run() is holding over the content of a.js and not function doStuff(data) in a_spec.js.
Illustration by example
Lets re-write a.js to:
var a = 'I am exported';
var b = 'I am not exported';
function foo () {
console.log(a);
console.log(this.b)
}
module.exports.a=a;
module.exports.foo=foo;
and a.spec.js to:
var one = require('./one');
console.log(one); // { a: 'I am exported', foo: [Function: foo] }
one.a = 'Charles';
one.b = 'Diana';
console.log(one); // { a: 'Charles', foo: [Function: foo], b: 'Diana' }
Now if we invoke one.foo() it will result in:
I am exported
Diana
The I am exported is logged to the console because console.log(a) inside foo points to var a inside the closure foo is holding over the contents of a.js.
The Diana is logged to the console because console.log(this.b) inside foo points to one.b in a.spec.js.
So what do you need to do to make it work?
You need to change:
module.exports = (function () {
return {
run,
doStuff
};
function run() {
return api()
.then((data) => {
return doStuff(data);
})
}
function doStuff(data) {
return `Got data: ${data}`;
}
})()
to:
module.exports = (function () {
return {
run,
doStuff
};
function run() {
return api()
.then((data) => {
return this.doStuff(data); // ´this.doStuff´ points to ´doStuff´ in the exported object
}.bind(this)) // to ensure that ´this´ does not point to the global object
}
function doStuff(data) {
return `Got data: ${data}`;
}
})()
Related
I have mocked / replaced a method on an object. During the test I want to replace the replacement so that it returns / async yields something new. Is there anyway to pass "force" or something like that to replace. I know that it's already replaced, please Sinon, just go ahead and replace the current replacement with this one.
import sinon from "sinon";
import * as bar from "./bar";
import * as foo from "./foo";
describe("availabilityWorker", () => {
describe("sync", () => {
let sandbox: sinon.SinonSandbox;
beforeEach(async () => {
sandbox = sinon.createSandbox();
sandbox.replace(bar, "bar", () => {});
sandbox.replace(foo, "foo", async function* () {
yield { error: undefined, data: { hello: "world" } };
});
});
afterEach(() => {
sandbox.restore();
});
it("should do what I tell it", async () => {
{
// part 1 - (throws errors here 👇)
sandbox.replace(foo, "foo", async function* () {
yield { error: undefined, data: { hello: "world2" } };
});
// ... assertions
}
{
// part 2
sandbox.replace(foo, "foo", async function* () {
yield { error: undefined, data: { hello: "world3" } };
});
// ... assertions
}
});
});
});
note I don't want to just do sandbox.restore() because that will restore everything, I just want to override foo
I have in file.js code which I simplify this way:
function Apple(,data) {
this.attr1 = data.attr1,
this.attr2 = data.attr2,
this.doSomething(data.flag);
}
Apple.prototype = new function() {
this.function1 = function() {
// do something
}
this.doSomething = function(flag) {
// return value
}
}
I want to test function1(), for that, I want to mock first doSomething() to return a specific value, but I am failing because any call to Apple() function executes immediately doSomething():
describe('Apple', () => {
test('test function1()', () => {
Apple.doSomething = jest.fn().mockImplementation((2) => {
// do something;
});
let apple = new Apple(data); // the original doSomething() executes here instead of the mocked version
});
});
How can I achieve my goal ?
Try using spyOn:
const mockDoSomething = jest.spyOn(Apple.prototype, 'doSomething').mockReturnValueOnce((flag) => {
// mock do something;
})
const parkReport = () => {
return {
averageAge: () => {
return console.log('Something');
}
}
};
const initialize = ( parkReport => {
return parkReport.averageAge();
})(parkReport);
In the IIFE initialize parkReport.averageAge() is showing an error of not a function. How do you call the nested AverageAge() from initialize?
You need to call the parkReport function. Here you are passing parkReport as a callback to the IIFE. so you need to call it to expect something returned from it.
const parkReport = () => {
return {
averageAge: () => {
return console.log('Something');
}
}
};
const initialize = (parkReport => {
return parkReport() // call it
.averageAge(); // and get the age
})(parkReport);
The test is failing on the first expectation. Is there a way to inject a spy to a function so that I can check if the function was called with correct arguments?
var myObj = {}
myObj.prop = function propFn () {
return 'foo'
}
myObj.func = function (disp) {
return disp(this.prop())
}
let disp = sinon.spy()
sinon.stub(myObj, 'prop').callsFake(function fakeFn () {
return 'bar'
})
expect(disp.called).to.be.true
disp.should.have.been.calledWith('bar')
Thanks!
Please try as below,
describe('prop', () => {
const myObj = {};
myObj.prop = function propFn() {
return 'foo';
};
myObj.func = function (disp) {
return disp(this.prop());
};
it('should be called', () => {
sinon.stub(myObj, 'prop').returns('bar');
const disp = sinon.spy();
myObj.func(disp);
expect(disp.callCount).to.equal(1);
expect(myObj.prop.callCount).to.equal(1);
expect(disp.callCount).to.equal(1);
expect(disp.calledWith('bar')).to.equal(true);
});
});
result
prop
✓ should be called
1 passing (975ms)
The code I'm testing is fairly simple: it invokes a method in case a condition is verified. If not, it invokes another method contained within the first one as an attribute.
app.js:
function test (fn, isActivated) {
if (isActivated) {
return fn('foo')
}
return fn.subFn('bar')
}
var fn = function (p) { return p }
fn.subFn = function (p) { return 'sub-' + p }
var resFn = test(fn, true)
var resSubFn = test(fn, false)
document.write(resFn) // shows 'foo' as expected
document.write(resSubFn) // shows 'bar' as expected
I've set a spy on each method but the spy on the fn method does not seem to work while the spy on the contained method subFn works. See below:
app.test.js:
'use strict'
const chai = require('chai')
const sinon = require('sinon')
const trigger = require('../app').trigger
chai.should()
describe('test app', function () {
before(function () {
this.fn = function () {}
this.fn.subFn = function () {}
this.subFnSpy = sinon.spy(this.fn, 'subFn')
this.fnSpy = sinon.spy(this.fn)
})
describe('isActivated is true', function () {
before(function () {
trigger(this.fn, true)
})
it('should invoke fn', function () {
this.fnSpy.callCount.should.equal(1) // return false because callCount = 0
})
})
describe('isActivated is false', function () {
before(function () {
trigger(this.fn, false)
})
it('should invoke subFn', function () {
this.subFnSpy.callCount.should.equal(1) // return false because callCount = 0
})
})
})
Smelling something wrong with the spy on the fn function, I've tried with two separate methods. Both spies fail in this case:
app.js:
exports.trigger = function (fn, subFn, isActivated) {
if (isActivated) {
return fn('fn')
}
return subFn('bar')
}
app.test.js
'use strict'
const chai = require('chai')
const sinon = require('sinon')
const trigger = require('../app').trigger
chai.should()
describe('test app', function () {
before(function () {
this.fn = function () {}
this.subFn = function () {}
this.fnSpy = sinon.spy(this.fn)
this.subFnSpy = sinon.spy(this.subFn)
})
beforeEach(function () {
this.fnSpy.reset()
this.subFnSpy.reset()
})
describe('isActivated is true', function () {
before(function () {
trigger(this.fn, this.subFn, true)
})
it('should invoke fn if isActivated is true', function () {
this.fnSpy.callCount.should.equal(1) // return false
})
})
describe('isActivated is false', function () {
before(function () {
trigger(this.fn, this.subFn, false)
})
it('should invoke subFn if isActivated is true', function () {
this.subFnSpy.callCount.should.equal(1) // return false
})
})
})
Any suggestion of what I'm doing wrong?
I did not find the exact solution but a workaround quite close from one. So the problem seems to lie with the way this.fn is handled withtin sinon.spy, thus instead of doing:
this.fnSpy = sinon.spy(this.fn)
this.subFnSpy = sinon.spy(this.subFn)
We do the following:
this.fnSpy = sinon.spy(this, 'fn')
this.subFnSpy = sinon.spy(this.fn, 'subFn')
The is easd by the fact that I use this to store fn and subFn.