Testing a controller

Last but not least, let's take a look at the image controller, and specifically test for the main index function. Since the index function does a lot of work and performs a number of different tasks, the test file will make extensive use of stubs and spies. The first thing we need to do before any tests is declare a number of global variables for our tests, as well as set up all of our stubs, spies, and placeholder objects for use with proxyquire. Then, we require the actual image controller using proxyquire. Create a file named tests/controllers/image.test.js and insert the following code:

let proxyquire = require('proxyquire'), 
callback = sinon.spy(),
sidebarStub = sinon.stub(),
fsStub = {},
pathStub = {},
md5Stub = {},
ModelsStub = {
Image: {
findOne: sinon.spy()
},
Comment: {
find: sinon.spy()
}
},
image = proxyquire('../../controllers/image', {
'../helpers/sidebar': sidebarStub,
'../models': ModelsStub,
'fs': fsStub,
'path': pathStub,
'md5': md5Stub
}),
res = {},
req = {},
testImage = {};

With this code, we define a number of global variables, such as spies, stubs, or empty placeholder JavaScript objects. Once our stubs are prepared, we will call proxyquire to include our image controller (ensuring that the required modules within the image controller are actually replaced with our various stubs and spies). Now that all of our globals, stubs, and spies are prepared, let's include some tests.

Include the following code after the preceding block of code:

describe('Image Controller', function(){ 
beforeEach(()=>{
res = {
render: sinon.spy(),
json: sinon.spy(),
redirect: sinon.spy()
};
req.params = {
image_id: 'testing'
};
testImage = {
_id: 1,
title: 'Test Image',
views: 0,
likes: 0,
save: sinon.spy()
};
});
// to do: write tests...
});

Once again, we will build up some setup using a beforeEach block for our tests. This sets spies on each of the res object's functions, including render, JSON, and redirect (each of these are used throughout the image controller). We fake the query string parameter by setting the req.params object with an image_id property. Finally, we will create a test image object that will be used by our fake mongoose image model stub to emulate a database object being returned from MongoDB:

describe('Index',()=>{ 
it('should be defined', ()=>{
expect(image.index).to.be.defined;
});
it('should call Models.Image.findOne', ()=>{
ModelsStub.Image.findOne = sinon.spy();
image.index(req, res);
expect(ModelsStub.Image.findOne).to.be.called;
});
it('should find Image by parameter id', ()=>{
ModelsStub.Image.findOne = sinon.spy();
image.index(req, res);
expect(ModelsStub.Image.findOne).to.be.calledWith(
{ filename: { $regex: 'testing' } },
sinon.match.func
);
});
// to do: write more tests...
});

The first test we run is to ensure that the index function actually exists. Within the index function, the very first action that occurs is that the image model is found via the Models.Image.findOne function. In order to test that function, we will need to first set it as spy. The reason we do this here and not in beforeEach is because we might want the findOne method to behave slightly differently in each test, so we don't want to set a strict rule to be applied for all the tests.

In order to emulate that a GET call was posted to our server and our image index controller function was hit, we can just fire the function manually. We do this using image.index(req, res) and pass in our fake request and response objects (defined earlier as globals and stubbed in the beforeEach function).

Since ModelsStub.Image.findOne is a spy, we can test that it was called, and then separately test that it was called specifically with the parameters we expect it to be called with. In the case of findOne, where the second parameter is a callback function, we don't care or want to test the very specific function that was included, but only ensure that an actual function was included. To do this, we can use Sinon's matcher API and specify that a func, or function, was included as the second parameter.

This last set of tests tests the code that executes when an image is found and returned from the findOne function:

describe('with found image model', ()=>{
beforeEach(function(){
ModelsStub.Image.findOne =
sinon.stub().callsArgWith(1,null,testImage);
});
it('should incremement views by 1 and save', ()=>{
image.index(req, res);
expect(testImage.views).to.equal(1);
expect(testImage.save).to.be.called;
});
it('should find related comments', ()=>{
image.index(req, res);
expect(ModelsStub.Comment.find).to.be.calledWith(
{image_id: 1},
{},
{ sort: { 'timestamp': 1 }},
sinon.match.func
);
});
it('should execute sidebar', ()=>{
ModelsStub.Comment.find =
sinon.stub().callsArgWith(3, null, [1,2,3]);
image.index(req, res);
expect(sidebarStub).to.be.calledWith(
{image: testImage, comments: [1,2,3]}, sinon.match.func);
});
it('should render image template with image and comments', ()=>{
ModelsStub.Comment.find = sinon.stub().callsArgWith(3, null, [1,2,3]);
sidebarStub.callsArgWith(1, {image: testImage, comments: [1,2,3]});
image.index(req, res);
expect(res.render).to.be.calledWith('image', {image: testImage, comments: [1,2,3]});
});
});

The first thing that you will note here is that findOne is no longer a spy in these tests, but a stub that will manually fire the callback function that's provided as its second parameter. The callback function that's fired will include our test image model. With this stub, we are emulating that the database call was in fact made via findOne and that a valid image model was returned. Then, we can test the remainder of the code that executes within that main callback. We perform a similar setup with the Comment.find call.

When the sidebarStub gets executed, we use the callsArgWith Sinon function, which fires the callback function that was originally included as a parameter. Within that callback function, we include fake viewModel as a parameter.

Once sidebarStub does its job, we expect res.render to have been called, and we specify the exact parameters we expect it to have been called with.

Running the tests for the image controller should yield the following output:

    $ npm test
Image Controller
Index
should be defined
should call Models.Image.findOne
should find Image by parameter id
with found image model
should incremement views by 1 and save
should find related comments
should execute sidebar
should render image template with image and comments
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.2.157