Reputation: 639
In my journey towards TDD, I am using Mocha, chai and sinon. There certainly is a learning curve there.
My goal is to write a test to verify that method4 was executed. How do I achieve that ?
//MyData.js
class MyData {
constructor(input) {
this._runMethod4 = input; //true or false
this.underProcessing = this.init();
}
method1() { return this.method2() }
method2() {
if (this._runMethod4) {
return this.method4();
} else {
return this.method3();
}
method4(){
return thirdPartyAPI.getData();
}
method3(){
return someAPI.fetchData();
}
init(){
return this.method1();
}
}
MyData.spec.js
describe('MyData', () => {
it('should execute method 4', function() {
let foo = new MyData(true);
foo.underProcessing.then(()=>{
// How do I verify that method4 was executed ??
expect(foo.method4.callCount).to.equal(1);
});
});
})
Upvotes: 5
Views: 7562
Reputation: 203359
Here's an example:
const expect = require('chai').expect;
const sinon = require('sinon');
const sinonTest = require('sinon-test');
sinon.test = sinonTest.configureTest(sinon);
sinon.testCase = sinonTest.configureTestCase(sinon);
describe("MyData", () => {
it("should execute method 4", sinon.test(function() {
let spy = this.spy(MyData.prototype, 'method4');
let foo = new MyData(true);
return foo.underProcessing.then(() => {
expect(spy.callCount).to.equal(1);
});
}));
});
As an additional requirement, I added sinon-test
because it's really useful to help clean up spies/stubs after the test has run.
The main feature is this line:
let spy = this.spy(MyData.prototype, 'method4');
This replaces MyData.prototype.method4
by a Sinon spy, which is a pass-through function (so it calls the original) that will record how exactly it was called, how often, with what arguments, etc. You need to do this before the instance is created, because otherwise you're too late (the method might already have been called through the chain of method calls that starts with this.init()
in the constructor).
If you want to use a stub instead, which is not pass-through (so it won't call the original method), you can do that as well:
let spy = this.stub(MyData.prototype, 'method4').returns(Promise.resolve('yay'));
So instead of calling thirdPartyAPI.getData()
and returning its result, method4
will now return a promise resolved with the value yay
.
The remaining code should speak for itself, with one caveat: an explicit return
in front of foo.underProcessing.then(...)
.
I assume that foo.underProcessing
is a promise, so it's asynchronous, which means that your test should be asynchronous as well. Since Mocha supports promises, when you return a promise from a test, Mocha will know how to deal with it properly (there's an alternative method of making an asynchronous test with Mocha, involving callback functions, but when you're testing promise-based code you shouldn't really use those since it's easy to run into timeouts or swallowed exceptions).
Upvotes: 5