Nodejs test image resizing with mocha - node.js

I've used graphicmagick to resize a image in my nodejs app.
The problem is when writing the unit tests, I cant seem to find any direction or examples on this. Does it make sense that I test the image resizing, seeing that I'm using a third-party module? If yes, how could I write a test for my code?
// dependencies
var async = require('async');
var AWS = require('aws-sdk');
var gm = require('gm').subClass({ imageMagick: true });
var util = require('util');
// get reference to S3 client
var s3 = new AWS.S3();
var _800px = {
width: 800,
destinationPath: "large"
};
var _500px = {
width: 500,
destinationPath: "medium"
};
var _200px = {
width: 200,
destinationPath: "small"
};
var _45px = {
width: 45,
destinationPath: "thumbnail"
};
var _sizesArray = [_800px, _500px, _200px, _45px];
var len = _sizesArray.length;
// handler for dev environment
exports.GruntHandler = function (filepath) {
console.log("Path to file is: " + filepath);
// get the file name
var srcFile = filepath.split("/").pop();
var dstnFile = "dst";
// Infer the image type.
var typeMatch = srcFile.match(/\.([^.]*)$/);
if (!typeMatch) {
console.error('unable to infer image type for key ' + srcFile);
return;
}
var imageType = typeMatch[1];
if (imageType != "jpg" && imageType != "png") {
console.log('skipping non-image ' + srcFile);
return;
}
for (var i = 0; i<len; i++) {
// Transform the image buffer in memory.
gm(filepath)
.resize(_sizesArray[i].width)
.write(dstnFile + "/" + _sizesArray[i].destinationPath + "/" + srcFile, function (err) {
if (err) {
console.log(err);
}
});
}
console.log(" grunt handler called!");
};

The convention when writing unit tests, is to test only the isolated unit.
All external dependencies should be stubbed/mocked, so you can check only the logic in your unit (in this case your unit is your module).
As to what to test, your unit's only public method is "GruntHandler", so this is the only method you should test, as it is the service that this unit provides to other units.
In order to replace all the module's dependencies, I like to use the proxyquire package. It replaces "require" calls with stubs you can control.
I've written an example test using the testing stack that I use personally: proxyquire, mocha, sinon for spies, chai for assertions.
Setup - put this in a "test-helper" module to require in all test files:
var chai = require('chai');
var sinonChai = require("sinon-chai");
var expect = chai.expect;
var sinon = require('sinon');
chai.use(sinonChai);
var proxyquire = require('proxyquire');
And in your test file:
require('./test-helper');
describe('Image Resizing module', function () {
var gmSubclassStub = sinon.stub();
var testedModule = proxyquire('path-to-tested-file', {
'gm': {subClass: sinon.stub().returns(gmSubclassStub)}
});
describe.only('GruntHandler', function () {
it("should call gm write with correct files", function () {
// Arrange
var filepath = 'pepo.jpg';
// Spies are the methods you expect were actually called
var write800Spy = sinon.spy();
var write500Spy = sinon.spy();
var write200Spy = sinon.spy();
var write45Spy = sinon.spy();
// This is a stub that will return the correct spy for each iteration of the for loop
var resizeStub = sinon.stub();
resizeStub.withArgs(800).returns({write:write800Spy});
resizeStub.withArgs(500).returns({write:write500Spy});
resizeStub.withArgs(200).returns({write:write200Spy});
resizeStub.withArgs(45).returns({write:write45Spy});
// Stub is used when you just want to simulate a returned value
gmSubclassStub.withArgs(filepath).returns({resize:resizeStub});
// Act - this calls the tested method
testedModule.GruntHandler(filepath);
// Assert
expect(write800Spy).calledWith("dst/large/pepo.jpg");
expect(write500Spy).calledWith("dst/medium/pepo.jpg");
expect(write200Spy).calledWith("dst/small/pepo.jpg");
expect(write45Spy).calledWith("dst/thumbnail/pepo.jpg");
});
});
});
More about sinon spies, stubs, and mocks: http://sinonjs.org/
Proxyquire: https://github.com/thlorenz/proxyquire
And a great tutorial about all this together: http://kroltech.com/2014/02/node-js-testing-with-mocha-chai-sinon-proxyquire/#.VPTS9fmUdV0

Related

NodeJs and Jest: Mock Mongoose multiple calls find().sort().limit()

Wondering how to mock multiple chained mongoose calls in jest. For example I have a simple method:
exports.Limit5Videos = async() => {
try{
const mostClicked = await Videos.find().sort({ clicks: -1 }).limit(5);
return mostClicked;
}catch(err){
return err;
}
};
This method returns the top 5 most clicked videos "on a website". I'm trying to unit test this method but when I do i keep getting the following:
TypeError: Videos.find(...).sort(...).limit is not a function when I run my test.
Here is my test:
var video1 = new Video();
video1.clicks = 1;
var video2 = new Video();
video2.clicks = 2;
var video3 = new Video();
video3.clicks = 3;
var video4 = new Video();
video4.clicks = 4;
var video5 = new Video();
video5.clicks = 5;
var video6 = new Video();
video6.clicks = 6;
var listOfVideos = [video1, video2, video3, video4, video5, video6];
var expected = [video6, video5, video4, video3, video2];
var mockedValue = listOfVideos.filter(v => v.clicks > 1).sort(function compare(a, b) {
return b.clicks - a.clicks;
});
Video.find = jest.fn().mockImplementation(() => ({ sort:
jest.fn().mockResolvedValue(mockedValue)}));
await expect(videoHelper.GetMostClickedLimit5()).resolves.toBe(expected);
How can I write a jest unit test for this method? Sorry if this is basic but new to jest and can't find anything out there on multiple chained calls. I can run the test using "sort" but can't figure out how to add "limit".

nodejs get function from vm and use normally

I have this code
// hello.js
var hello = function() {
return 'hello world';
};
var helloDate = function() {
console.log(new Date());
return 'hello world';
};
loaded with a nodejs vm
//test/test_hello.js
var sinon = require('sinon');
var vm = require('vm');
var fs = require('fs');
var source = fs.readFileSync('./hello.js').toString();
var local = new vm.createContext({});
var script = new vm.Script(source);
script.runInContext(local);
var localModule = {};
for (var o in local) {
localModule[o] = local[o];
}
before(function() {
});
it('hello', function() {
local.hello();
});
it('helloDate', function () {
localModule.helloDate();
});
but with I use localModule.helloDate() I get this error message
$ mocha
✓ hello
1) helloDate
1 passing (7ms)
1 failing
1) helloDate:
ReferenceError: console is not defined
at Object.helloDate (evalmachine.<anonymous>:6:3)
at Context.<anonymous> (test/test_hello.js:26:15)
How can I use helloDate "normally" in nodejs? like use a function of a module.
Try to pass console to vm context, like so:
var local = new vm.createContext({console: console});

mocha test sends `test` as variable to node app

When writing the tests for my entry file, index.js I run into the problem that the command mocha test passes test as an argument to index.js as it uses process.argv to receive parameters to run on a development environment. I had thought that by using something like minimist to name the parameters would fix this, however this problem still remains when running the tests. In this way my tests do not use the object provided in my test suits, as shown in the following code.
How do I get around this, so that when running my tests, it uses the event object I provide in my test set-up and not the command mocha test?
index.js
'use strict';
var _ = require("underscore");
var async = require('async');
var argv = require("minimist")(process.argv.slice(2));
var getprotocol = require("./getProtocol");
var _getprotocol = getprotocol.getProtocol;
var S3rs = require("./S3resizer");
var s3resizer = S3rs.rs;
var objCr = require("./objectCreator");
var createObj = objCr.creator;
var fileRs = require("./fileResizer");
var fileResizer = fileRs.rs;
var configs = require("./configs.json");
var mkDir = require("./makeDir");
var makeDir = mkDir.handler;
exports.imageRs = function (event, context) {
var _path = argv.x || event.path; //argv.x used to be process.argv[2]
console.log("Path, %s", _path);
var _dir = argv.y; // used to be process.argv[3]
console.log(_dir);
var parts = _getprotocol(_path);
var imgName = parts.pathname.split("/").pop();
console.log("imgName: %s", imgName);
var s3Bucket = parts.hostname;
var s3Key = imgName;
var _protocol = parts.protocol;
console.log(_protocol);
// RegExp to check for image type
var imageTypeRegExp = /(?:(jpg)|(png)|(jpeg))$/;
var sizesConfigs = configs.sizes;
var obj = createObj(_path);
// Check if file has a supported image extension
var imgExt = imageTypeRegExp.exec(s3Key);
if (imgExt === null) {
console.error('unable to infer the image type for key %s', s3Key);
context.done(new Error('unable to infer the image type for key %s' + s3Key));
return;
}
var imageType = imgExt[1] || imgExt[2];
// Do more stuff here
};
if (!process.env.LAMBDA_TASK_ROOT) {
exports.imageRs();
}
test.js
describe("imgeRs", function () {
var getprotocol = require("../getProtocol");
var S3rs = require("../S3resizer");
var objCr = require("../objectCreator");
var mkDir = require("../makeDir");
var fileResizer = require("../fileResizer");
describe("Calling S3", function () {
describe("Success call", function () {
var testedModule, eventObj, contextDoneSpy, S3resizerStub, objCreatorStub, getProtocolStub, fakeResults, mkDirStub, fileResizerStub;
before(function (done) {
contextDoneSpy = sinon.spy();
S3resizerStub = sinon.stub(S3rs, "rs");
objCreatorStub = sinon.stub(objCr, 'creator');
getProtocolStub = sinon.stub(getprotocol, "getProtocol");
mkDirStub = sinon.stub(mkDir, "handler");
fileResizerStub = sinon.stub(fileResizer, "rs");
eventObj = {"path": "s3://theBucket/image.jpeg"};
fakeResults = ["resized"];
testedModule = proxyquire("../index", {
'./getProtocol': {
'getProtocol': getProtocolStub
},
'./S3resizer': {
'rs': S3resizerStub
},
'./objectCreator': {
'creator': objCreatorStub
},
'./makeDir': {
'handler': mkDirStub
},
'./fileResizer': {
'rs': fileResizerStub
}
});
S3resizerStub.callsArgWith(5, null, fakeResults);
testedModule.imageRs(eventObj, {done: function (error) {
contextDoneSpy.apply(null, arguments);
done();
}});
});
after(function () {
S3rs.rs.restore();
objCr.creator.restore();
getprotocol.getProtocol.restore();
mkDir.handler.restore();
fileResizer.rs.restore();
});
it("calls context.done with no error", function () {
expect(contextDoneSpy).has.been.called;
});
});
});
});

NodeJS - How to test index.js without module.exports

I'm testing my NodeJs project using Mocha and I have a file, index.js that is the main file without module.exports that is run like a CLI
index.js
// CLI tools
var bluebird = require('bluebird');
var gigatool = require('./lib/gigatool');
var debug = require('debug')('index');
var size = 20;
var page = process.env.BATCH;
var startDate = process.env.START;
var dataDir = process.env.DATADIR;
debug(page, startDate, dataDir);
// requires parameters
if (!process.env.BATCH) {
throw new Error('BATCH environment variable is needed');
}
tool = gigatool(size, page, dataDir);
bluebird.all([tool.clean(startDate), tool.continuous()])
.finally(function(){
process.exit(0);
});
test.js
'use strict';
var chai = require('chai');
var fs = require('fs');
var noop = require('lodash.noop');
var rimraf = require('rimraf');
var async = require('async');
var rimraf = require('rimraf');
var expect = chai.expect;
describe.only('Integration', function() {
var dataDir = './countries';
var path = dataDir + '/Albania';
describe('clean run', function() {
this.timeout(10000);
before(function() {
process.env.BATCH = 1;
process.env.DEBUG = '*';
require('../../index');
});
after(function(done) {
// rimraf(dataDir, done);
});
});
});
if I run require('./index'), it will run the module and then continue to move forward, how can i wait for it to end before i run test cases?
Note: It is calling some apis
You need to test your whole application at once, this is still testing but hardly "unit" testing unless your code is a unit ("the unix way"). For this reason your code should start with:
var Promise= require("bluebird");
var exec= Promise.promisify(require("child_process").exec);
var run = function(args){
return exec("node", ["../../index.js"].concat(args)).get("stdout");
};
Which would make your tests test the actual inputs on the file:
describe('your code', function() {
it('should work with params a,b', function(){
return run(['a','b']).then(function(out){ // note the Mocha promise syntax
assert.equal(out, 'your expected stdout');
});
});
});
Unfortunately, there is no way to unit test individual aspects of a CLI Node script as you have it. Instead, what I've done in the past is have conditional execution based on whether the script was used via require or called from the command line:
// index.js
var foo = require('foo');
var bar = require('bar');
// ...
// determine if this script is being required as a module or is CLI
var IS_EXECUTING = (require.main === module);
var methods = {
init: function(args) {
methods.auditArgs(args);
methods.doSomeStuff(arg1, arg2);
methods.doOtherStuff();
},
auditArgs: function(args) {/* ... */},
doSomeStuff: function(arg1, arg2) {/* ... */},
// ...
};
// At the bottom we either begin execution or return a function which can
// be called in a test harness when ready...
if (IS_EXECUTING) {
methods.init(process.argv);
} else {
module.exports = function (mockMethods) {
// you could have some code here to mock out provided methods
// for example:
methods.auditArgs = mockMethods.auditArgs || methods.auditArgs;
// then return the "API" for this script...
return methods;
};
}
In your test harness then you would simply require the file and when ready, use it like you would any other module. But when called from the command line the code will just execute normally:
// in test.js
'use strict';
var chai = require('chai');
// ...
var appFactory = require('index');
var expect = chai.expect;
describe('initialization', function() {
var app;
beforeEach(function() {
app = appFactory({
auditArgs = chai.spy(function() { });
// other mock method implementations, spies, etc
});
});
it('should call necessary methods on init', function() {
expect(app.auditArgs).to.have.been.called(1);
// ...
});
});

How to spy on a property that is not exported

I have a module "sitescollection" like this:
var site = require('./site'); // <- this should be stubbed
var sitesCollection = function(spec) {
var that = {};
that.sites = {};
that.findOrCreateById = function(id) {
if (typeof(that.sites[id]) == "undefined") {
that.sites[id] = site({id: id}); // <- its used here
}
return that.sites[id];
};
return that;
};
module.exports = sitesCollection;
so within sitescollection, site is a module that is not exported. But inside the code, i use it. Now i'm writing jasmine specs for #findOrCreateById().
I want to spec my the findOrCreateBy() function. But i want to stub the site() function, because the spec should be independent from the implementation. Where do i have to create the spyed method on?
var sitescollection = require('../../lib/sitescollection');
describe("#findOrCreateById", function() {
it("should return the site", function() {
var sites = sitescollection();
mysite = { id: "bla" };
// Here i want to stub the site() method inside the sitescollection module.
// spyOn(???,"site").andRetur(mysite);
expect(sites.findOrCreateById(mysite.id)).toEqual(mysite);
});
});
You can achieve this using https: //github.com/thlorenz/proxyquire
var proxyquire = require('proxyquire');
describe("#findOrCreateById", function() {
it("should return the site", function() {
// the path '../../lib/sitescollection' is relative to this test file
var sitesCollection = proxyquire('../../lib/sitescollection', {
// the path './site' is relative to sitescollection, it basically
// should be an exact match for the path passed to require in the
// file you want to test
'./site': function() {
console.log('fake version of "./site" is called');
}
});
// now call your sitesCollection, which is using your fake './site'
var sites = sitesCollection();
var mysite = {
id: "bla"
};
expect(sites.findOrCreateById(mysite.id)).toEqual(mysite);
});
});

Resources