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);
// ...
});
});
Related
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});
I don't have experience at all with nodejs and I am learning. I have a code, which I am testing and giving me the following errors, and I don't understand why:
1) Part 3 Assessment Tests "before all" hook:
ReferenceError: wagner is not defined
at Object. (fx.js:2:31)
at Object. (dependencies.js:3:10)
at Context. (test.js:29:20)
2) Part 3 Assessment Tests "after all" hook:
TypeError: Cannot read property 'close' of undefined
at Context. (test.js:65:11)
These are the programs involved in the issue:
test.js --------------
var assert = require('assert');
var express = require('express');
var fs = require('fs');
var status = require('http-status');
var superagent = require('superagent');
var wagner = require('wagner-core');
var URL_ROOT = 'http://localhost:3000';
var PRODUCT_ID = '000000000000000000000001';
describe('Part 3 Assessment Tests', function() {
var server;
var app;
var succeeded = 0;
var finalCharge;
var Category;
var Config;
var fx;
var Product;
var Stripe;
var User;
before(function() {
app = express();
// Bootstrap server
models = require('./models')(wagner);
dependencies = require('./dependencies')(wagner);
// Make models available in tests
var deps = wagner.invoke(function(Category, fx, Product, Stripe, User, Config) {
return {
Category: Category,
fx: fx,
Product: Product,
Stripe: Stripe,
User: User,
Config: Config
};
});
...
dependencies.js -------------
var wagner = require('wagner-core');
var fs = require('fs');
var fx = require('./fx')(wagner);
var Stripe = require('stripe');
module.exports = function(wagner) {
var stripe =
// TODO: Make Stripe depend on the Config service and use its `stripeKey`
// property to get the Stripe API key.
wagner.factory('Stripe', function() {
return Stripe(Config.stripeKey);
});
wagner.factory('fx', fx);
wagner.factory('Config', function() {
return JSON.parse(fs.readFileSync('./config.json').toString());
});
var Config = wagner.invoke(function(Config) {
return Config;
});
};
fx.js --------------------------
var superagent = require('superagent');
var _ = require('underscore')(wagner);
module.exports = function(Config) {
...
};
I think I shouldn't have any problem because wagner is defined on test.js and passed as parameter to dependencies.js, and it is passing it on to fx.js.
1- Could you tell me what is wrong in the code ?
2- The second error, I have not cue why is it happening.
Please, help
In your dependencies.js, in the fx definition, you dont need to pass wagner as parameter, because that variable doesnt exist there:
var fx = require('./fx');
As i see, you already defined the Config factory, and you dont need to assign it to a variable for use it, because wagner will autoinject your "Config" factory, allowing you access to it:
wagner.factory('Stripe', function(Config) {
return Stripe(Config.stripeKey);
});
...
wagner.factory('Config', function() {
return JSON.parse(fs.readFileSync('./config.json').toString());
});
Then in your fx.js, when you wanna use the Config parameter you just pass it:
module.exports = function(Config) {
var url = 'http://openexchangerates.org/api/latest.json?app_id=' +
Config.openExchangeRatesKey;
...
}
Exactly the same you should do if you wanna use the "Config" factory in any other file:
[auth.js]
function setupAuth(User, Config, app) {
...
}
You're trying to use wagner in fx.js before you execute the exported function and outside of the scope that wagner is passed to. Also, Config seems a bit misleading if you're passing wagner to that function instead.
If you can defer loading underscore, you could do something like:
var superagent = require('superagent');
var _;
module.exports = function(Config) {
if (!_)
_ = require('underscore')(Config);
// ...
};
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;
});
});
});
});
How can i initialize async.queue "for" the global module scope?
The example bellow shows the main problem, that qq is undefined, not yet known or its only defined locally in a function scope.
The target is to access a "module-global" q in different module member functions.
So creating a module pattern version of the example in https://github.com/caolan/async#queue
I know why the // not working-code isn't valid, it is only too show which declaration ideas i tried.
Additional i m aware of how to solve the problem by using a different pattern, but that wouldn't answer the question.
var mymodule = (function() {
'use strict';
var async = require('async');
// var q = async.queue(mymodule.qq); // not working
// var q ; // not working
var mymodule = {
// q = async.queue(this.qq); // not working
init: function() {
// var q = async.queue(this.qq); // local not global
// q = async.queue(this.qq); // not working
q.drain = function() {
console.log('all items have been processed');
}
},
add: function(task) {
this.q.push(task);
},
qq: function(task, callback) {
console.log(task);
callback();
},
};
return mymodule;
}());
'use strict';
var async = require('async');
var mymodule = function(){
//This will be you constructor
//You can do something like this
this.queue = async.queue(function(task, callback){
console.dir(task);
}, 4);
};
//Now start adding your methods
mymodule.prototype.add = function(task){
this.queue.push(task, function(){});
};
mymodule.prototype.qq = function(task, callback){
// ..
callback()
};
//export it
module.exports = mymodule;
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);
});
});