Unit Test with Mongoose - node.js

I'm new to Node.js, Mongoose, and testing in this environment. I have the following schema declared in a separate file.
Issue = mongoose.model("Issue", {
identifier: String,
date: String,
url: String,
name: String,
thumbnailURL: String
});
Then I have this method which simply returns all of the Issue instances in the MongoDB collection.
function issues(request, response) {
response.setHeader('Content-Type', 'text/json');
Issue.find().sort('date').exec(function(error, items) {
if (error) {
response.send(403, {"status": "error", "error:": exception});
}
else {
response.send(200, {"issues": items});
}
});
}
I've gotten this far through experimentation, and now I want to test it, but I've run into a problem. How do I go about testing it, without setting up a MongoDB connection, etc. I know that I can set all that stuff up, but that's an integration test. I want to write unit tests to test things like:
Does the function set the content type correctly
Does the function sort by the date field
Does the function return a 403 when an error occurs?
... and so on
I'm curious to see how I could refactor my existing code to make it more unit testable. I've tried maybe creating a second function that's called through, accepting the response and Item schema objects as parameters, but it doesn't feel right. Anyone have any better suggestions?

Mongoose model (your Issue) returns a new instance of the Query object. The new query instance has access to the exec method through the prototype. (mongoose 3.8~)
If you want to return an error you can write:
sinon.stub(mongoose.Query.prototype, "exec").yields({ name: "MongoError" }, null);

Using mocha with chaijs and sinonjs in my node code something like this method works for me:
var should = require('chai').should(),
sinon = require('sinon'),
mongoose = require('mongoose');
it('#issues() handles mongoosejs errors with 403 response code and a JSON error message', function (done) {
// mock request
var request = {};
// mock response
var response = {};
response.setHeader = function(header) {};
response.send = function (responseCode, jsonObject) {
responseCode.should.equal(403);
jsonObject.stats.should.equal('error');
// add a test for "error:": exception
done();
}
var mockFind = {
sort: function(sortOrder) {
return this;
},
exec: function (callback) {
callback('Error');
}
}
// stub the mongoose find() and return mock find
mongoose.Model.find = sinon.stub().returns(mockFind);
// run function
issues(request, response);
});

I'm not sure how to test the Content-Type, and I haven't tested this code myself, but I'm happy to help out if it doesn't work. It seems to make sense to me. Basically we just created a callback so we could move the response.send out of the actual custom logic, then we can test via that callback. Let me know if it doesn't work or make sense. You can use the links that the other guys posted to prevent having to connect to the db.
Issue = mongoose.model("Issue", {
identifier: String,
date: String,
url: String,
name: String,
thumbnailURL: String
});
function issues(callback, request, response) {
Issue.find().sort('number').exec(function(error, items) {
if (error) {
callback(403, {"status": "error", "error:": exception});
}
else {
callback(200, {"issues": items});
}
});
}
//Note: probably don't need to make a whole new `sender` function - depends on your taste
function sender(statusCode, obj) {
response.setHeader('Content-Type', 'text/json');
response.send(statusCode, obj);
}
//Then, when you want to implement issues
issues(sender, request, response);
//The tests - will depend on your style and test framework, but you get the idea
function test() {
//The 200 response
issues(function(code, obj) {
assert(code === 200);
assert(obj.issues === ??); //some testable value, or just do a truthy test
//Here, you can also compare the obj.issues item to the proper date-sorted value
});
//The error response
issues(function(code, obj) {
assert(code === 403);
assert(code.status === 'error');
});
}

A good place to start would be:
Investigate the concepts around stubs and mocks, and test doubles.
Check out Sinon.js which is the Mocking framework of choice for Node.JS

Related

Mocking with Sinon against a service module

I have got myself to the stage of unit testing, and to be honest, with all the different examples online I have got myself confused. I have a good understanding of Mocha & Chai, but Sinon is a different story.
So I have what I think is a pretty straight forward setup. I have a POST route that calls a controller. This controller is like so (removed some basic validation code)
const { createUser } = require('../services/user.service');
const apiResponse = require('../helpers/apiResponse');
const postUser = async (req, res) => {
const user = {
account_id: req.body.id,
status: req.body.status,
created_at: new Date(),
updated_at: new Date(),
};
const result = await createUser(user);
return apiResponse.successResponseWithData(res, 'User added.', result.affectedRows);
} catch (err) {
return apiResponse.errorResponse(res, err);
}
};
module.exports = {
postUser,
};
So all it really does is validate, and then creates a user object with the req and pass that to a service class. This services class does nothing more than pass the data to a database class.
const { addUserToDb } = require('../database/user.db');
const createUser = async (user) => {
try {
const createdUser = await addUserToDb(user);
return createdUser;
} catch (err) {
throw new Error(err);
}
};
module.exports = {
createUser,
};
I wont show the database class because what I want to focus on first is the controller, and then I can hopefully do the rest myself.
So from what I understand, I should be testing functions. If a function makes an external call, I should spy, mock, stub that call? I should only spy, mock or stub this functions dependencies, if one of the dependencies
has its own dependency (like the service module above having a database call dependency), this should be performed in another test? Sorry, just a few questions to help me understand.
Anyways, so I have created a user.controller.test.js file. I have not got far with it, but this is what I have so far
const chai = require('chai');
const sinon = require('sinon');
const { expect } = chai;
const faker = require('faker');
const controller = require('../controllers/user.controller');
const service = require('../services/user.service');
const flushPromises = () => new Promise(setImmediate);
describe('user.controller', () => {
describe('postUser', () => {
beforeEach(() => {
//I see a lot of code use a beforeEach, what should I be doing here?
});
it('should create a user when account_id and status params are provided', async () => {
const req = {
body: { account_id: faker.datatype.uuid(), status: 'true' },
};
const stubValue = {
id: faker.datatype.id(),
account_id: faker.datatype.uuid(),
status: 'true',
created_at: faker.date.past(),
updated_at: faker.date.past(),
};
});
});
});
If I am being totally honest I am pretty lost as to what I should be testing here. From my understanding, I need to mock the service module I think.
Could someone kindly provide some insight as to what I should be doing in this test?
Many thanks
Update
Thank you for your detailed response, I have managed to get a spy working which is a step forward. So I want to do a test on my service module, createUser method.
You can see that my createUser method takes a user Object as a parameter and passes this to a database module where it is inserted into the database and then the user object returned.
So when testing my service class, I need to mock this call to my database module.
const chai = require('chai');
const sinon = require('sinon');
const { expect } = chai;
const faker = require('faker');
const service = require('../services/user.service');
const database = require('../database/user.db');
describe('user.service', () => {
describe('createUser', () => {
it('should create a user when user object is provided', async () => {
const user = {
id: faker.datatype.string(),
status: 'true',
created_at: faker.date.past(),
updated_at: faker.date.past(),
};
const expectedUser = {
id: user.id,
status: user.status,
created_at: user.created_at,
updated_at: user.updated_at,
};
const mockedDatabase = sinon.mock(database);
mockedDatabase.expects('addUserToDb').once().withArgs(expectedUser);
await service.createUser(user);
mockedDatabase.verify();
mockedDatabase.restore();
});
});
});
When I test this, I seem to be getting this response, and it still seems to be inserting the record into my database.
ExpectationError: Expected addUserToDb({
id: 'yX7AX\\J&gf',
status: 'true',
created_at: 2020-06-03T03:10:23.472Z,
updated_at: 2020-05-24T14:44:14.749Z
}, '[...]') once (never called)
at Object.fail (node_modules\sinon\lib\sinon\mock-expectation.js:314:25)
Do you have any idea what I am doing wrong?
Thanks
before I try, I would like to suggest to drop the try/catch blocks everywhere, I will assume you're using expressJs in your Node application, and for such, take a look at express-promise-router as using that Router (instead the default one) will automatically catch anything it was thrown and you just need to focus on the code...
taking your example, you would write:
const { addUserToDb } = require('../database/user.db');
const createUser = async (user) => addUserToDb(user);
module.exports = {
createUser,
};
and
const { createUser } = require('../services/user.service');
const apiResponse = require('../helpers/apiResponse');
const postUser = async (req, res) => {
const { id: account_id, status } = res.body;
const result = await createUser({ account_id, status }); // set the date in the fn
return apiResponse.successResponseWithData(res, 'User added.', result.affectedRows);
};
module.exports = {
postUser,
};
if there's an error and in some place on the route an error is thrown, you will get a nice message back in the response with the error
regarding the code it self, seems a lot cleaner to read - keep in mind that code is for humans, the machine does not even care how you name your variables 😊
Now, regarding the tests ... I do tend to split things into 3 parts
unit tests: the functions itself, single one, like validation, helpers, etc
integration tests: when you call your API endpoint what should be returned
GUI tests (or end-to-end/e2e): applied when a GUI exists, will skip this for now
so in your case, the first thing to make sure of is what are you testing... and taking that, start from the small blocks (unit tests) and move up to the blocks that make sure all is glued together (e2e)
So all it really does is validate, and then creates a user object with the req and pass that to a service class. This services class does nothing more than pass the data to a database class.
Seems a great way to start, so it "validates" ... let's test our validation, let's pass null, undefined, string when all you want is int and so on, until we get a pretty good idea that whatever it passes, we will reply correctly with and without an error
Note I tend to use OpenAPI specs, which makes things easier for me as it provides 2 things
documentation of the endpoints
validation of the endpoints with a nice error message to the consumer
and yes, I always test some validation just to make sure it's working as expected, even though I trust the tool 100% 😜
So from what I understand, I should be testing functions.
well, an application is a group of functions, so all good there 💪
If a function makes an external call, I should spy, mock, stub that call?
I'll try to explain as best as I can what spies, stubs and mocks in Sinon are, please be gentle 🙏
Spies
they tell us information about functions calls, like, number of times called, arguments, return value, and more - they have two types, anonymous spies or spies that wrap methods in our code
function testMyCallback(callback) { callback(); }
describe('testMyCallback fn', function() {
it('should call the callback', function() {
const callbackSpy = sinon.spy(); // anonymous spy - no arguments
testMyCallback(callbackSpy);
expect(callbackSpy).to.have.been.calledOnce;
});
});
const user = {
setNname: function(name) {
this.name = name;
}
}
describe('setname fn', function() {
it('should be called with name', function() {
const setNameSpy = sinon.spy(user, 'setName'); // wrap method spy
user.setName('Katie');
expect(setNameSpy).to.have.been.calledOnce;
expect(setNameSpy).to.have.been.valledWith('Katie');
setNameSpy.restore(); // to remove the Spy and prevent future errors
});
});
Stubs
are power-spies, as they have all the functionality of Spies, but they replace the target function, they have methods that can return a specific value or throw a specific exception and a bit more
they are great to be used with your question regarding external calls, as they replace calls (so you can mock the call behavior and never use the original call)
the simplest of the examples is:
function isAdult(age) {
return age > 21;
}
describe('Sinon Stub Example', () => {
it('should pass', (done) => {
const isAdult = sinon.stub().returns('something');
isAdult(0).should.eql('something');
isAdult(0).should.not.eql(false);
done();
});
});
we've STUB'ed our function, and explicitly said it's a "function" that returns a string something... and for now on, we will never need to go to the function itself, as we have STUB it, we've replaced the real behavior with our own
another example of using STUBs when calling our API application in our integration tests
describe('when we stub our API call', () => {
beforeEach(() => {
this.get = sinon.stub(request, 'get'); // stub "request.get" function
});
afterEach(() => {
request.get.restore(); // remove our power-spy
});
describe('GET /api/v1/accounts', () => {
const responseObject = {
status: 200,
headers: {
'content-type': 'application/json'
}
};
const responseBody = {
status: 'success',
data: [
{
accountId: 1,
status: 'active'
},
{
accountId: 2,
status: 'disabled'
}
]
};
it('should return all accounts', (done) => {
// the 3 objects of our callback (err, res, body)
this.get.yields(null, responseObject, JSON.stringify(responseBody));
request.get(`${base}/api/v1/movies`, (err, res, body) => {
expect(res.statusCode).to.be.eql(200);
expect(res.headers['content-type']).to.contain('application/json');
body = JSON.parse(body);
expect(body).to.be.an('array').that.includes(2);
done();
});
});
});
});
you can also stub axios, but you will need a new library, either moxios, or proxyquire or more...
Mocks
are a bit similar to Stubs (our Power-Spies) but they can be used to replace whole objects and alter their behavior, they are mostly used when you need to stub more than one function from a single object - if all you need is to replace a single function, a stub is easier to use
Mocks can make things oversimplify and you could break your application without even knowing, so be aware...
a normally use is, for example
function setupNewAccount(info, callback) {
const account = {
account_id: info.id,
status: info.status,
created_at: new Date(),
updated_at: new Date()
};
try { Database.save(account, callback); }
catch (err) { callback(err); }
}
describe('setupNewAccount', function() {
it('', function() {
const account = { account_id: 1, status: 'active' };
const expectedAccount = {
account_id: account.id, status: account.status
};
const database = sinon.mock(Database);
database.expectes('save').once().withArgs(expectedAccount);
setupNewAccount(account, function() {});
database.verify();
database.restore();
});
});
something that we will keep forgetting is the .restore() part, and for that, there's a package (one more...) called sinon-test that will auto cleanup at the end of a test
I just hope it helped you with some of your questions and it's a bit clearer now 😏
BTW, for stubbing HTTP requests, I use nock as I think it's much easier to read and use than Sinon, especially for anyone that is reading code for the first time and has no experience in either Sinon or Nock...

google-geocoder with proxyquire and sinon

I'm still very much learning node, js, sinon, proxyquire, etc.
I have a module that uses the google-geocode module (https://github.com/bigmountainideas/google-geocoder) and I am struggling to write a test to stub it.
This all boils down I think to how you set it up. In time.js I do as follows as per google-geocoder documentation:
var geocoder = require('google-geocoder');
...
module.exports = function(args, callback) {
var geo = geocoder({ key: some-thing });
geo.find('new york', function(err, response) { ... });
}
I'm trying to test as follows but I get the error:
TypeError: geo.find is not a function
at run (cmdsUser/time.js:x:x)
at Context.<anonymous> (tests/cmdsUser/time-test.js:x:x)
time-test.js:
var time;
var findStub;
before(function () {
findStub = sinon.stub()
time = proxyquire('./../../cmdsUser/time',{ 'google-geocoder': { find: findStub } } );
});
describe('Demo test', function() {
it('Test 1', function(done){
findStub.withArgs('gobbledegook').yields(null, { this-is: { an-example: 'invalid' } });
time(['gobbledegook'], function(err, response) {
expect(response).to.equals('No result for gobbledegook');
done();
});
});
});
I am a little confused. Many thanks.
google-geocode's exports seem to be formatted as:
{
function() {
[...]
// Will return an instance of GeoCoder
}
GeoCoder: {
[...]
__proto__: {
find: function() {
// Replace me!
}
}
},
GeoPlace: [...]
}
proxyquire seems to replace the function that returns the instance even when wrapping find in an object with the key "GeoCoder" which brings you closer to the solution by actually assigning a method find to the correct object. I made a test project to try to learn the best way to overcome this and I felt kinda stuck. But since you were callThru'ing before, you might as well do proxyquire's dirty work then pass the stubbed version of the dependency instead.
before(function() {
// Stub, as you were before
findStub = sinon.stub()
// Require the module yourself to stub
stubbedDep = require('google-geocoder')
// Override the method with the extact code used in the source
stubbedDep.GeoCoder.prototype.find = findStub
// Pass the stubbed version into proxyquire
test = proxyquire('./test.js', { 'google-geocoder': stubbedDep });
});
I really hope there's a better way to do what you'd like. I believe class' constructors act in a similar manner and that makes me think others have a similar issue (see issues below). You should probably join that conversation or another on that repo and post an answer back here for others if this is still an active project of yours over a half a year later with no response.
Issues: #136, #144, #178

Testing Express and Mongoose with Mocha

I'm trying to test my REST API endpoint handlers using Mocha and Chai, the application was built using Express and Mongoose. My handlers are mostly of the form:
var handler = function (req, res, next) {
// Process the request, prepare the variables
// Call a Mongoose function
Model.operation({'search': 'items'}, function(err, results) {
// Process the results, send call next(err) if necessary
// Return the object or objects
return res.send(results)
}
}
For example:
auth.getUser = function (req, res, next) {
// Find the requested user
User.findById(req.params.id, function (err, user) {
// If there is an error, cascade down
if (err) {
return next(err);
}
// If the user was not found, return 404
else if (!user) {
return res.status(404).send('The user could not be found');
}
// If the user was found
else {
// Remove the password
user = user.toObject();
delete user.password;
// If the user is not the authenticated user, remove the email
if (!(req.isAuthenticated() && (req.user.username === user.username))) {
delete user.email;
}
// Return the user
return res.send(user);
}
});
};
The problem with this is that the function returns as it calls the Mongoose method and test cases like this:
it('Should create a user', function () {
auth.createUser(request, response);
var data = JSON.parse(response._getData());
data.username.should.equal('some_user');
});
never pass as the function is returning before doing anything. Mongoose is mocked using Mockgoose and the request and response objects are mocked with Express-Mocks-HTTP.
While using superagent and other request libraries is fairly common, I would prefer to test the functions in isolation, instead of testing the whole framework.
Is there a way to make the test wait before evaluating the should statements without changing the code I'm testing to return promises?
You should use an asynchronous version of the test, by providing a function with a done argument to it.
For more details refer to http://mochajs.org/#asynchronous-code.
Since you don't want to modify your code, one way to do that could be by using setTimeout in the test to wait before to call done.
I would try something like this:
it('Should create a user', function (done) {
auth.createUser(request, response);
setTimeout(function(){
var data = JSON.parse(response._getData());
data.username.should.equal('some_user');
done();
}, 1000); // waiting one second to perform the test
});
(There might be better way)
Apparently, express-mocks-http was abandoned a while ago and the new code is under node-mocks-http. Using this new library it is possible to do what I was asking for using events. It's not documented but looking at the code you can figure it out.
When creating the response object you have to pass the EventEmitter object:
var EventEmitter = require('events').EventEmitter;
var response = NodeMocks.createResponse({eventEmitter: EventEmitter});
Then, on the test, you add a listener to the event 'end' or 'send' as both of them are triggered when the call to res.send. 'end' covers more than 'send', in case you have calls other than res.send (for example, res.status(404).end().
The test would look something like this:
it('Should return the user after creation', function (done) {
auth.createUser(request, response);
response.on('send', function () {
var data = response._getData();
data.username.should.equal('someone');
data.email.should.equal('asdf2#asdf.com');
done();
});
});

How do I sinon.stub a nested method with a callback?

I need to test a method that includes a sub-method which makes a call to an API server. I’d like to stud this internal sub-method, but I can’t seem to do that. Here’s an example:
var requests = require('./requests.js');
var utilityClass = {
methodCreatesObject: function (callback) {
// Here’s the method I’m trying to stub:
requests.makeCallToAPI(requestObject, function (err, responseFromAPI) {
doSomethingWithResponse(responseFromAPI, function (err, finalObject) {
if (err) {
callback(err, null);
} else {
callback(null, finalObject); // <- Want to test the value of finalObject
}
});
});
}
}
So, my test looks something like this (updated to show loading requests.js before utility.js):
var should = require('should'),
Joi = require('joi'),
sinon = require('sinon'),
requests = require('../lib/modules/requests.js'),
utility = require('../lib/modules/utility.js')
;
// Start my tests:
describe('Method', function () {
before(function () {
var fakeAPIresponse = { ... }
sinon.stub(requests, 'makeCallToAPI').yield(null, fakeAPIresponse);
});
it('should produce a well-formed finalObject', function (done) {
utilityClass.methodCreatesObject(function (err, response) {
if (err) {
done(err);
} else {
response.should.do.this.or.that;
done();
}
});
});
});
As I understand it, .yields() should try to run the first callback it detects in the arguments and feed its own arguments to it (resulting in doSomethingWithResponse(responseFromAPI, function () {...})). However, when running mocha, I’m getting an error indicating that the API server could not be reached, which suggests that the real requests.makeCallToAPI() is being called, and not my stub.
I must be missing something. What am I doing wrong here?
Where are you requiring the request.js? You will need to require request.js before you load up the module you want to test.
Edit 1: Using sinon.js
Here is a gist of what I meant: https://gist.github.com/limianwang/1114249de99c6a189384
Edit 2: Using proxyquire
If you are intending to test simply the utilities without concern of what actually happens within the requests.makeAPICall, you can use something like proxyquire to do the trick. If you are concerned with the actual logic within requests.js, you can use sinon.stub to stub out the actual request.get api.

Is there any way to mock this function with Jasmine or Sinon?

I'm testing a module, and I want to mock out a a dependency within that module. Let me frame my scenario, if I may:
In my module
myModule.prototype.func = function(callback) {
complexObj.doStuff('foo', function(err) {
callback(err, 'stuff');
});
};
So, I'm trying to basically mock complexObj. It doesn't really matter if I mock the entire object or just the doStuff function in this case. Let's assume that doStuff does something like interact with a web service or the filesystem. complexObj is being injected into myModule by dependency injection. I've been using Jasmine and Sinon to try to mock or stub this object and function, but I've had no luck, so I've resorted to something like this, which seems a little kludgy:
In my spec:
describe('Testing myModule', function() {
it('should do stuff', function() {
ComplexObj.prototype.doStuff = function(arg, callback) {
callback(null); // If no errors, 'doStuff' returns null indicating no errors
};
var complexObj = new ComplexObj();
new myModule(complexObj).func(function(err, results) {
// Set up expectations...
});
});
});
So, as you can see, I'm psuedo-mocking out the doStuff function in the ComplexObj object. Since I'm not concerned about ComplexObj or doStuff function, I'm just invoking the callback with 'null' indicating to func that there were no errors in doStuff. As I mentioned before, I feel there should be a better way to handle this? Suggestions?
With Jasmine, you would do something like this:
var complexObj = {doStuff: null};
spyOn(complexObj, 'doStuff');
new myModule(complexObj).func(function(err, results) {
expect(complexObj.doStuff).toHaveBeenCalledWith(args, callback);
});
Edit: Or you could set up expectations in your mocked doStuff:
var complexObj = {doStuff: null};
spyOn(complexObj, 'doStuff').andCallFake(function(args, callback) {
expect(args).toEqual(/*...*/);
expect(callback).toEqual(/*...*/);
callback();
});
new myModule(complexObj).func(function(err, results) {
expect(complexObj.doStuff).toHaveBeenCalled();
});

Resources