How to stub constructor - javascript

I am writing uni-test and tring to isolate my code as much as possible. I am using Mocha and chai for writing test and ES5 Syntax.
The problem is, I am not able to find any solution to stub constructor.
Q.reject(new Error(obj.someFunction());
In above example, I know how to test promise, how to stub my inner function, but how I can stub Error constructor? And how I can check callWithExactly(), etc.
oStubSomeFunction = sinon.stub(obj, "someFunction")
I am using for normal function. I didn't find any relevant example in ChaiDocumentation

If you really want to stub the Error constructor you can use the Node.js global namespace:
code.js
exports.func = function() {
return new Error('error message');
}
code.test.js
const { func } = require('./code');
const sinon = require('sinon');
const assert = require('assert');
describe('func', function () {
it('should create an Error', function () {
const errorStub = sinon.stub(global, 'Error'); // <= stub the global Error
errorStub.callsFake(function(message) { // <= use callsFake to create a mock constructor
this.myMessage = 'stubbed: ' + message;
});
const result = func();
errorStub.restore(); // <= restore Error before doing anything else
assert(result.myMessage === 'stubbed: error message'); // Success!
sinon.assert.calledWithExactly(errorStub, 'error message'); // Success!
});
});
If you do this you'll want to restore Error before doing absolutely anything else...even assertions work using Error so the original Error needs to be in place in case an assertion fails.
It's a lot safer to just spy on Error:
const { func } = require('./code');
const sinon = require('sinon');
describe('func', function () {
it('should create an Error', function () {
const errorSpy = sinon.spy(global, 'Error'); // <= spy on the global Error
func();
sinon.assert.calledWithExactly(errorSpy, 'error message'); // Success!
errorSpy.restore();
});
});
...and ultimately there is a limit to how isolated code under test can get. For something as low-level as Error you might want to look at leaving it completely intact and testing for a thrown Error using something like Chai's .throw assertion.

Related

Javascript sinon testing callback

I'm trying to test that certain function is called in a callback, however I don't understand how to wrap the outer function. I'm using mocha as my testing suite, with chai as my assertion library, and sinon for my fakes.
fileToTest.js
const externalController = require('./externalController');
const processData = function processData( (req, res) {
externalController.getAllTablesFromDb( (errors, results) => {
// call f1 if there are errors while retrieving from db
if (errors) {
res.f1();
} else {
res.f2(results);
}
)};
};
module.exports.processData = processData;
In the end I need to verify that res.f1 will be called if there are errors from getAllTablesFromDb, and res.f2 will be called if there are no errors.
As can be seen from this snippet externalController.getAllTablesFromDb is a function that takes a callback, which in this case I have created using an arrow function.
Can someone explain how I can force the callback down error or success for getAllTablesFromDb so that I can use a spy or a mock to verify f1 or f2 was called?
var errorSpy = sinon.spy(res, "f1");
var successSpy = sinon.spy(res, "f2");
// your function call
// error
expect(errorSpy.calledOnce);
expect(successSpy.notCalled);
// without errors
expect(errorSpy.notCalled);
expect(successSpy.calledOnce);
One possible solution is to extract the callback then force that down the desired path of failure or success. This extraction can be done using a npm package called proxyquire. The callback is extracted by removing the require line from the beginning of the file then:
const proxyquire = require('proxyquire');
const externalController = proxyquire('path to externalController',
'path to externalController dependency', {
functionToReplace: (callback) => { return callback }
}
});
const extractedCallback = externalController.getAllTablesFromDb(error, results);
Then you can call extractedCallback with the parameters you want.
extractedCallback(myArg1, myArg2);
and set a spy on res.f1 and res.f2
sinon.spy(res, 'f1');
And do any assertion logic you need to.

testing a function that has been returned by a promise - checking for errors

I am testing a a function that is returned as a part of a promise. I'm using chai-as-promised.
I'm able to test that the function works but I am unable to test that it throws errors properly.
The function I'm trying to test, leaving out lots of code related to the promise:
// function that we're trying to test
submitTest = (options) => {
// missingParam is defined elsewhere. It works - the error is thrown if screenshot not passed
if (missingParam(options.screenShot)) throw new Error('Missing parameter');
return {};
}
My tests:
describe('SpectreClient()', function () {
let client;
before(() => client = SpectreClient('foo', 'bar', testEndpoint));
// the client returns a function, submitTest(), as a part of a promise
/*
omitting tests related to the client
*/
describe('submitTest()', function () {
let screenShot;
before(() => {
screenShot = fs.createReadStream(path.join(__dirname, '/support/test-card.png'));
});
// this test works - it passes as expected
it('should return an object', () => {
const submitTest = client.then((response) => {
return response.submitTest({ screenShot });
});
return submitTest.should.eventually.to.be.a('object');
});
// this test does not work - the error is thrown before the test is evaluated
it('it throws an error if not passed a screenshot', () => {
const submitTest = client.then((response) => {
return response.submitTest({});
});
return submitTest.should.eventually.throw(Error, /Missing parameter/);
});
});
})
The output of the test -
// console output
1 failing
1) SpectreClient() submitTest() it throws an error if not passed a screenshot:
Error: Missing parameter
How do I test that the error is thrown correclty? I'm not sure if it's a mocha problem or a promises thing or a chai-as-promised thing. Help much appreciated.
Exceptions raised inside promise handlers are converted to promise rejections. submitTest executes inside the callback to client.then, and consequently the exception it raises becomes a promise rejection.
So you should do something like:
return submitTest.should.be.rejectedWith(Error, /Missing parameter/)

How to unit test a function which calls another that returns a promise?

I have a node.js app 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 when trying to unit test it.
Basically, I would like to make sure that based on what my service returns, I flush the response with the right status code and body.
So with mocha and sinon it looks something like:
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 have tested my similar scenarios when the function I am testing returns itself a promise since I can hook my assertions in the then.
I also have tried to wrap controller.findAll with a Promise and resolve it from the response.send but it didn't work neither.
You should move your assert section into the res.send method to make sure all async tasks are done before the assertions:
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);
}
},
};
The idea here is to have the promise which service.findAll() returns accessible inside the test's code without calling the service. As far as I can see sinon-as-promised which you probably use does not allow to do so. So I just used a native Promise (hope your node version 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;
});
When code is difficult to test it can indicate that there could be different design possibilities to explore, which promote easy testing. What jumps out is that service is enclosed in your module, and the dependency is not exposed at all. I feel like the goal shouldn't be to find a way to test your code AS IS but to find an optimal design.
IMO The goal is to find a way to expose service so that your test can provide a stubbed implementation, so that the logic of findAll can be tested in isolation, synchronously.
One way to do this is to use a library like mockery or rewire. Both are fairly easy to use, (in my experience mockery starts to degrade and become very difficult to maintain as your test suite and number of modules grow) They would allow you to patch the var service = require('./category.service'); by providing your own service object with its own findAll defined.
Another way is to rearchitect your code to expose the service to the caller, in some way. This would allow your caller (the unit test) to provide its own service stub.
One easy way to do this would be to export a function contstructor 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 to exercise the findAll method. Removing the need for an asynchronous test altogether.

testing promised service with Chai and Sinon

I'm stuck with testing promies in Chai and Sinon. Generally I got service with is wrapper for xhr request and it returns promises. I tried to test it like that:
beforeEach(function() {
server = sinon.fakeServer.create();
});
afterEach(function() {
server.restore();
});
describe('task name', function() {
it('should respond with promise error callback', function(done) {
var spy1 = sinon.spy();
var spy2 = sinon.spy();
service.get('/someBadUrl').then(spy1, spy2);
server.respond();
done();
expect(spy2.calledOnce).to.be.true;
expect(sp2.args[0][1].response.to.equal({status: 404, text: 'Not Found'});
});
});
My notes about this:
// spy2 is called after expect finish assertion
// tried with var timer = sinon.useFakeTimers() and timer.tick(510); with no results
// tried with chai-as-promised - don’t know how use it :-(
// cannot install sinon-as-promised only selected npm modules available in my environment
Any any ideas how fix this code/ test this service module?
There's various challenges here:
if service.get() is asynchronous, you need to wait for its completion before checking your assertions;
since the (proposed) solution checks the assertions in a promise handler, you have to be careful with exceptions. Instead of using done(), I would opt for using Mocha's (which I assume you're using) built-in promise support.
Try this:
it('should respond with promise error callback', function() {
var spy1 = sinon.spy();
var spy2 = sinon.spy();
// Insert the spies as resolve/reject handlers for the `.get()` call,
// and add another .then() to wait for full completion.
var result = service.get('/someBadUrl').then(spy1, spy2).then(function() {
expect(spy2.calledOnce).to.be.true;
expect(spy2.args[0][1].response.to.equal({status: 404, text: 'Not Found'}));
});
// Make the server respond.
server.respond();
// Return the result promise.
return result;
});

Stubbing a promisified function with sinon and bluebird

In the file I would like to test, I have the following code:
var httpGet = Promise.promisify(require("request").get);
httpGet(endpoint, {
auth: {bearer: req.body.access_token},
json: true
})
.then(...)
Now, in my tests, I want to make sure that httpGet was called once, and make sure the parameters are valid. Before being promisified, my test looked like this:
beforeEach(function () {
request.get = sinon.stub()
.yields(null, null, {error: "test error", error_description: "fake google error."});
});
afterEach(function () {
expect(request.get).to.have.been.calledOnce();
var requestArgs = request.get.args[0];
var uri = requestArgs[0];
expect(uri).to.equal(endpoint);
//...
});
Unfortunately this no longer works when request.get is promisified. I tried stubbing request.getAsync instead (since bluebird appends "Async" to promisified functions), but that does not work either. Any ideas?
Promise.promisify doesn't modify the object, it simply takes a function and returns a new function, it is completely unaware that the function even belongs to "request".
"Async" suffixed methods are added to the object when using promisify All
Promise.promisifyAll(require("request"));
request.getAsync = sinon.stub()
.yields(null, null, {error: "test error", error_description: "fake google error."});
expect(request.getAsync).to.have.been.calledOnce();
Just for future reference I've solved this a bit differently, and I think a little cleaner. This is typescript, but basically the same thing.
fileBeingTested.ts
import * as Bluebird from 'bluebird';
import * as needsPromise from 'needs-promise';
const methodAsync = Bluebird.promisify(needsPromise.method);
export function whatever() {
methodAsync().then(...).catch(...);
}
test.spec.ts
import * as needsPromise from 'needs-promise';
import * as sinon form 'sinon';
const methodStub = sinon.stub(needsPromise, method);
import { whatever } from './fileBeingTested';
Then you use the methodStub to manage what calls happen. You can ignore that it's being promisified and just manage it's normal behavior. for example if you need it to error.
methodStub.callsFake((arg, callback) => {
callback({ error: 'Error' }, []);
});
The promisified version will throw the error and you'll get it in the catch.
Anyone coming across this. I have small utility func
function stubCBForPromisify(stub) {
let cbFn = function() {
let args = [...arguments];
args.shift();
return stub(...args);
};
return cbFn.bind(cbFn, () => ({}));
}
In test
var getStub = sinon.stub().yields(null, {error: "test error", error_description: "fake google error."})
sinon.stub(require("request"), 'get', stubCBForPromisify(getStub))
expect(getStub).to.have.been.calledOnce();
I was running into trouble testing this using tape and proxyquire. I'm not sure what pattern/framework people are using that allowed them to modify the required'd request object directly as shown in the accepted answer. In my case, in the file I want to test I require('jsonFile'), then call bluebird.promisifyAll(jsonFile). Under normal conditions this creates a readFileAsync method that I want to stub. However, if during testing I try to use proxyquire to pass in a stub, the call to promisifyAll overwrites my stub.
I was able to fix this by also stubbing promisifyAll to be a no-op. As shown this might be too coarse if you rely on some of the async methods to be created as-is.
core.js:
var jsonFile = require('jsonfile');
var Promise = require('bluebird');
Promise.promisifyAll(jsonFile);
exports.getFile = function(path) {
// I want to stub this method during tests. It is
// created by promisifyAll
return jsonFile.readFileAsync(path);
}
core-test.js:
var proxyquire = require('proxyquire');
var tape = require('tape');
var sinon = require('sinon');
require('sinon-as-promised');
tape('stub readFileAsync', function(t) {
var core = proxyquire('./core', {
'jsonfile': {
readFileAsync: sinon.stub().resolves({})
},
'bluebird': { promisifyAll: function() {} }
});
// Now core.getFile() will use my stubbed function, and it
// won't be overwritten by promisifyAll.
});

Categories