I have a function as shown below:
function test(parms) {
var self = this;
return this.test2(parms)
.then(function (data) {
if (data) {
return ;
}
else {
return Bluebird.delay(1000)
.then(self.test.bind(self, parms));
}
}.bind(self));
};
I am trying to write unit tests for this function. I am using sinon.stub
to mock the functionality of the function test2
.
I wrote a test case where test2
returns true
and therefore the test
function successfully completes execution. However I want a test case where on the first instance test2
returns false
, it waits for delay and next time test2
returns true
. For that I wrote my test case as below:
var clock;
var result;
var test2stub;
var count = 0;
before(function () {
clock = sinon.useFakeTimers();
//object is defined before
test2stub = sinon.stub(object,"test2", function () {
console.log("Count is: " + count);
if (count === 0) {
return (Bluebird.resolve(false));
}
else if (count === 1) {
return (Bluebird.resolve(true));
}
});
clock.tick(1000);
object.test("xyz")
.then(function (data) {
result = data;
});
clock.tick(1000);
count = count + 1;
clock.tick(1000);
});
after(function () {
test2stub.restore();
clock.restore();
});
it("result should be undefined. Check if test2 returned false first & true next",
function () {
expect(result).to.be.undefined;
});
In the logs it shows that count has value 0 only.
The code of test
is actually incorrect. It never returns data on success. It returns undefined
. The function should return data on success otherwise you won't be able to use it as parameter for next .then
handler
.then(function (data) {
if (data) {
return data;
}
Next you make wrong assumptions about function test
. It will NEVER return undefined
. The function is rather dangerous and will call itself forever in an endless chain of promises until it squeezes out any not-null data from test2
.
One shouldn't launch test code in before
or beforeEach
section. before
and after
are meant to prepare the environment like faking timers and then restoring them.
One reason for calling tested code in the it
handler is because promises should be handled differently. The handler should accept a parameter which indicates that the test will be asynchronous and the the test engine gives it a timeout (usually 10 secs) to complete. The test is expected to either call done()
to indicate test is successful or call done(error)
if it failed and there is an error
object (or expect
threw an exception).
Also you should move the fake timer after the async operation started. In your code actually the first clock.tick
is useless.
There is a trick with using fakeTimers
. You can move time manually however it doesn't move on its own. For the first tick it works well. The promise is executed. However upon returning .delay(1000)
promise, there will be no command to move the time forward. So, to finish the test correctly (not to modify tested code) you have also to stub Bluebird.delay
I would change the stubs implementation and do something like this
describe("test2", function(){
beforeEach(function(){
clock = sinon.useFakeTimers();
test2stub = sinon.stub(object,"test2", function () {
console.log("Count is: " + count);
return (Bluebird.resolve((count++) > 0));
});
var _delay = Bluebird.delay.bind(Bluebird);
bluebirdDelayStub = sinon.stub(Bluebird,"delay", function (delay) {
var promise = _delay(delay);
clock.tick(1000);
return promise;
});
})
it("should eventually return true", function (done) {
object.test("xyz")
.then(function (data) {
expect(data).to.be.true;
expect(count).to.equal(2);
done();
})
.catch(function(err){
done(err);
});
clock.tick(1000);
});
after(function () {
test2stub.restore();
clock.restore();
bluebirdDelayStub.restore();
});
})
PS I verified this code under Node.js 0.10.35 and Bluebird 2.9.34
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With