how to mock object created via require - javascript

I'm new to node.js and in most code I've seen I don't see IoC/DI style constructor injection of dependencies.
Instead, typically the node.js extension require() is used to create local vars allowing access to external modules' exports.
But when writing unit-tests ( that isolate single layer/function ), how to mock the modules accessed via vars created with require ?
/helpers/dataHelper.js
var dataModel = require('../models/dataModel.js');
var getFormattedDataForRegion = function(region, callback) {
var data = {};
// validate region
// query dataModel
// async.map format data items
// callback(data);
}
/tests/dataHelperTests.js
describe('dataHelper', function(){
it('getFormattedDataForRegion returns expected response', function(done){
var expectedData = {};
// populate expectedData
// **** need some way to mock dataModel *****
dataHelper.getFormattedDataForRegion("west", function(data){
expect(data).to.eql(expectedData);
done();
});
});

This is done with proxyquire.
I personally don't like the technique, but it's the best way that I've found to respect the "node way" and still be able to test easily. You'd do:
var proxyquire = require('proxyquire'),
dataModelMock = require('./mocks/dataModel');
proxyquire('path/to/helpers/dataHelper.js', { '../models/dataModel.js': dataModelMock });

Related

Node.js - Pass context through to nested objects in function properties

I need to make a cleanly name-spaced module in Node.js, break it up into sub-modules via require() and pass the root context/scope all the way to the methods in the name-spaces.
// index.js
var SDK = function(data){
this.data = data;
};
SDK.prototype.Utilities = require('./utilities');
module.exports = function(data) {
return new SDK(data);
};
// utilities.js
module.exports = {
callAPI: function() {
console.log(this.data);
}
};
// Use-case (somewhere in the node.js app...)
var SDK = require('./index')("this is some data");
SDK.Utilities.callAPI();
// Should return "this is some data"
For example:
this in SDK needs to be accessible in SDK.Utilities.callAPI();, like below. Utilities is the name-space I'm referring to.
I'm aware of javascript bind() and call() techniques, but I'm not sure how to use them in this particular case.
You should use apply or call
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call
these run the function with the given this scope.

Stub out module function

Edit: Being a little bit more precise.
I want to test usecases for a Github API wrapper extension, that our team has created. For testing, we don't want to use API wrapper extension directly, so we want to stub out its functions. All calls to the API wrapper should be stubbed out for the tests, not just creating a clone stub.
I have a module "github" in Node.js:
module.exports = function(args, done) {
...
}
And I am requiring it like this:
var github = require('../services/github');
Now, I would like to stub out github(...) using Sinon.js:
var stub_github = sinon.stub(???, "github", function (args, callback) {
console.log("the github(...) call was stubbed out!!");
});
But sinon.stub(...) expects from me to pass an object and a method and does not allow me to stub out a module that is a function.
Any ideas?
There might be a way to accomplish this in pure Sinon, but I suspect it would be pretty hacky. However, proxyquire is a node library that is designed for solving these sort of issues.
Supposing you want to test some module foo that makes use of the github module; you'd write something like:
var proxyquire = require("proxyquire");
var foo = proxyquire(".foo", {"./github", myFakeGithubStub});
where myFakeGithubStub can be anything; a complete stub, or the actual implementation with a few tweaks, etc.
If, in the above example, myFakeGithubStub has a property "#global" set as true, (i.e. by executing myFakeGithubStub["#global"] = true) then the github module will be replaced with the stub not only in the foo module itself, but in any module that the foo module requires. However, as stated in the proxyquire documentation on the global option, generally speaking this feature is a sign of poorly designed unit tests and should be avoided.
I found that this worked for me...
const sinon = require( 'sinon' );
const moduleFunction = require( 'moduleFunction' );
// Required modules get added require.cache.
// The property name of the object containing the module in require.cache is
// the fully qualified path of the module e.g. '/Users/Bill/project/node_modules/moduleFunction/index.js'
// You can get the fully qualified path of a module from require.resolve
// The reference to the module itself is the exports property
const stubbedModule = sinon.stub( require.cache[ require.resolve( 'moduleFunction' ) ], 'exports', () => {
// this function will replace the module
return 'I\'m stubbed!';
});
// sidenote - stubbedModule.default references the original module...
You have to make sure that you stub the module (as above) before it's required elsewhere...
// elsewhere...
const moduleFunction = require( 'moduleFunction' );
moduleFunction(); // returns 'I'm stubbed!'
Simplest solution is to refactor your module:
instead of this:
module.exports = function(args, done) {
...
}
do this:
module.exports = function(){
return module.exports.github.apply(this, arguments);
};
module.exports.github = github;
function github(args, done) {
...
}
Now you can require it with:
const github = require('../services/github.js');
//or
const github = require('../services/github.js').github;
To stub:
const github = require('../services/github.js');
let githubStub = sinon.stub(github, 'github', function () {
...
});
If you are doing
var github = require('../services/github');
in global scope, then you can using 'global' as the object and 'github' as the method to be stubbed out.
var stub_github = sinon.stub(global, "github", function (args, callback) {
console.log("the github(...) call was stubbed out!!");
});

Structure for unit testing on node.js with mongoose

I've been developing with node.js for months but now I'm starting a new project and I'd like to know how to structure the app.
My problem comes when talking about unit testing. I will use nodeunit to write unit tests.
Also I'm using express to define my REST routes.
I was thinking about writing my code that access databases in two "separate" files (They will be more, obviously, but I'm just trying to simplify the code). There will be the routes code.
var mongoose = require('mongoose')
, itemsService = require('./../../lib/services/items-service');
// GET '/items'
exports.list = function(req, res) {
itemsService.findAll({
start: req.query.start,
size: req.query.size,
cb: function(offers) {
res.json(offers);
}
});
};
And, as I'm using there, an item service used just to access data layer. I'm doing this to test only data access layer on unit testing. It'll be something like this:
var mongoose = require('mongoose')
, Item = require('./../mongoose-models').Item;
exports.findAll = function(options) {
var query = Offer
.find({});
if (options.start && options.size) {
query
.limit(size)
.skip(start)
}
query.exec(function(err, offers) {
if (!err) {
options.cb(offers);
}
})
};
This way I can check with unit testing if it works correctly and I can use this code everywhere I want. The only thing I'm not sure if it's been correctly done is the way I pass a callback function to use returned value.
What do you think?
Thanks!
Yes, quite easily!
You can use a unit testing module like mocha and either node's own assert or another such as should.
As an example of a test case for your example model:
var ItemService = require('../../lib/services/items-service');
var should = require('should');
var mongoose = require('mongoose');
// We need a database connection
mongoose.connect('mongodb://localhost/project-db-test');
// Now we write specs using the mocha BDD api
describe('ItemService', function() {
describe('#findAll( options )', function() {
it('"args.size" returns the correct length', function( done ) { // Async test, the lone argument is the complete callback
var _size = Math.round(Math.random() * 420));
ItemService.findAll({
size : _size,
cb : function( result ) {
should.exist(result);
result.length.should.equal(_size);
// etc.
done(); // We call async test complete method
}
},
});
it('does something else...', function() {
});
});
});
And so on, ad nauseum.
Then when you're done writing your tests - assuming you've $ npm install mocha'd - then you'd simply run $ ./node_modules/.bin/mocha or $ mocha if you used npm's -g flag.
Depends how rectal/detailed you want to be really. I've always been advised to, and find it easier to: Write the tests first, to get a clear specification perspective. Then write the implementation against the tests, with any extra insight a freebie.

Overriding functions in other modules in node.js

I'm trying to stub a function with nodeunit in a Node.js app. Here's a simplified version of what I'm trying to do:
In lib/file.js:
var request = require('request');
var myFunc = function(input, callback){
request(input, function(err, body){
callback(body);
});
};
In test/test.file.js:
var file = require('../lib/file');
exports['test myFunc'] = function (test) {
request = function(options, callback){
callback('testbody');
};
file.myFunc('something', function(err, body){
test.equal(body, 'testbody');
test.done();
});
};
It seems like I'm not overriding request properly, because when I try to run the test, the actual non-stub request is getting called, but I can't figure out what the correct way to do it is.
EDIT:
To expand on Ilya's answer below, with my example above.
in lib/file/js:
module.exports = function(requestParam){
return {
myFunc: function(input, callback){
requestParam(input, function(err, body){
callback(body);
});
}
}
}
Then in test/test.file.js:
var fakeRequestFunc = function(input, callback){
// fake request function
}
var file = require('../lib/file')(fakeRequestFunc)(
//test stuff
}
As you noticed, variables declared in one module, cannot easily be accessed from another module. In such cases, you have two common variants:
1) Declare everything you need in every module (not your case, I suppose)
2) Pass parameters to a function
var ab = "foo",
index = require('/routes/index')(ab);
When you call a function form a module, you may pass it 'request' or any other vars or object you need.
I've run into similar issue. After exploring request module code my solution was using request.get instead of request in my code (do exactly the same). And then stub it in test like that: https://github.com/anatoliychakkaev/resizer-app/blob/master/test/resizer.js#L25
It also possible to stub result of 'require' method in nodejs. Check sources on lib/module.js to manage how to do it. It should be something like:
require('module')._cache['/path/to/request.js'] = your_stub
But I don't like this solution because it doesn't work in 100% of cases and may stop working in future versions of node (this is not public api), so you should use this way only in case when it's not possible to use other ways of stubbing.

Express - Passing mysql connection to scripts

I defined mysql connection with all parameters necessary to app.js, how can make visible to other scripts in routes/ by default, without requiring or redefining mysql parameters, just using client.query(..)?
A pattern I use is to set up my db object in a module once and export it: (let's call it utils/mySQL.js)
//I haven't used real mysql in node so excuse the pseudo-syntax:
var db = require('mysql-driver-thingy');
db.connect('localhost', 'sqlport', options...);
db.otherSetupFunctions();
console.log("Finished db setup. You should only see this message once! Cool.");
module.exports = db;
And then I can require the db object everywhere I need it. Since requires are cached, this does't actually call the setup methods multiple times.
In app.js:
var db = require('./utils/mySQL.js');
...
In models/user.js:
var db = require('../utils/mySQL.js');
...
A final option, which isn't recommended, is to pollute the global namespace. This seems to be the answer you're really after:
//set up your db
...
// and now make it available everywhere:
global.client = db.client
You can now magically use the client object in all your modules, without even requiring it.
There are many reasons globals are bad, though:
If your code and other code define globals, they could conflict and overwrite each other.
It's hard to find where you defined the db/client object, etc.
You can inject mysql connection into other scripts like this:
app.js
var mysqlConnection = new Conection(params);
require('controller/main.js)(mysqlConnection);
main.js
module.exports = function(mysqlConnection) {
// You can access your mysql connection here
};
UPDATE:
You can inject several variables same way. Also you still can export methods from module if you need this:
app.js
var mysqlConnection = new Conection(params);
var news = require('model/news.js)(app, mysqlConnection);
news.list(function(err, news) {
// Do something
});
news.js
module.exports = function(app, mysqlConnection) {
var methods = {};
// mysql connection and app available from here
methods.list = function(cb) {
mysqlConnection.list(function(err, data) {
cb(err, data);
});
};
return methods;
};

Categories

Resources