How is unit test a function that calls another that returns a promise?

I have a node.js application using express 4 and this is my controller:

var service = require('./category.service'); module.exports = { findAll: (request, response) => { service.findAll().then((categories) => { response.status(200).send(categories); }, (error) => { response.status(error.statusCode || 500).json(error); }); } }; 

It calls my service, which returns a promise. Everything works, but I am having trouble trying to unit test it.

Basically, I would like to make sure that depending on what my service returns, I clear the response with the correct status code and body.

So, with mocha and sinon, it looks something like this:

 it('Should call service to find all the categories', (done) => { // Arrange var expectedCategories = ['foo', 'bar']; var findAllStub = sandbox.stub(service, 'findAll'); findAllStub.resolves(expectedCategories); var response = { status: () => { return response; }, send: () => {} }; sandbox.spy(response, 'status'); sandbox.spy(response, 'send'); // Act controller.findAll({}, response); // Assert expect(findAllStub.called).to.be.ok; expect(findAllStub.callCount).to.equal(1); expect(response.status).to.be.calledWith(200); // not working expect(response.send).to.be.called; // not working done(); }); 

I tested my similar scenarios when the function I'm testing promises, as I can consolidate my statements at that time.

I also tried wrapping controller.findAll with a promise and resolving it from response.send, but it does not work either.

+5
source share
3 answers

You must transfer your assert section to the res.send method res.send that all asynchronous tasks run before statements:

 var response = { status: () => { return response; }, send: () => { try { // Assert expect(findAllStub.called).to.be.ok; expect(findAllStub.callCount).to.equal(1); expect(response.status).to.be.calledWith(200); // not working // expect(response.send).to.be.called; // not needed anymore done(); } catch (err) { done(err); } }, }; 
+6
source

The idea here is to have a promise that service.findAll() returns accessibility inside the test code without calling service . As far as I understand, the sinon-as-promised that you are probably using does not allow this. So I just used the native Promise (hope your version of node is not too old for it).

 const aPromise = Promise.resolve(expectedCategories); var findAllStub = sandbox.stub(service, 'findAll'); findAllStub.returns(aPromise); // response = { .... } controller.findAll({}, response); aPromise.then(() => { expect(response.status).to.be.calledWith(200); expect(response.send).to.be.called; }); 
+1
source

When code is difficult to test, it may indicate that there may be different design capabilities for learning that facilitate simple testing. What pops up is that service enclosed in your module, and the dependency is not displayed at all. I feel that the goal should not be to find a way to test your code AS IS, but to find the optimal design.

IMO The goal is to find a way to expose service so that your test can provide a wrapped implementation so that the findAll logic can be tested in isolation synchronously.

One way to do this is to use a library like mockery or rewire . Both of them are fairly easy to use (in my experience, the mockery begins to deteriorate and becomes very difficult to maintain as the test suite and the number of modules expand). They will allow you to fix var service = require('./category.service'); by providing your own service object with your own findAll .

Another way is to rebuild your code in order to somehow render the service caller. This will allow your subscriber (unit test) to provide their own service stub.

One easy way to do this is to export the contrructor function instead of an object.

 module.exports = (userService) => { // default to the required service this.service = userService || service; this.findAll = (request, response) => { this.service.findAll().then((categories) => { response.status(200).send(categories); }, (error) => { response.status(error.statusCode || 500).json(error); }); } }; var ServiceConstructor = require('yourmodule'); var service = new ServiceConstructor(); 

Now the test can create a stub for service and provide it to the ServiceConstructor for implementing the findAll method. Removing the need for asynchronous testing in general.

+1
source

All Articles