Using the --globals variable with MochaJS testing suite - javascript

I am trying to do a variant of end-to-end testing on a sailjs system using mocha. What I want to do is to simulate the action program flow by doing things like creating a user, and then performing other action with that user.
I'd like to be able to separate my tests into separate files that run in order in relation to different operations, such as "register new user" etc. To do this I need to be able to pass values between testing files.
Mocha contains an option setting --globals <value1, value2, etc>. Here is the description from the docs:
--globals allow the given comma-delimited global [names]
However, I've been unable to get this to work. Here's what I've tried. I have a bootstrap.test.js file that does basic before and after operations, starting and stopping sails:
var Sails = require('sails'),
sails;
before(function(done) {
Sails.lift({
log: {
level: 'error'
}
}, function(err, server) {
sails = server;
if (err) return done(err);
// here you can load fixtures, etc.
done(err, sails);
});
});
after(function(done) {
// here you can clear fixtures, etc.
sails.lower(done);
});
Then let's say I have two test files a.js and b.js that will run consecutively and for testing purposes contain very little:
a.js:
var user = 'some user';
b.js:
console.log( user );
If I then run mocha --globals, I get the error:
ReferenceError: user is not defined
What am I doing wrong here? I have been unable to find anywhere on the web a description of how this would be used.

You've misunderstood the purpose of --globals. It may be used when you use --check-leaks. The --check-leaks option checks whether the tests are leaking variables into the global space. Consider this suite:
it("one", function () {
foo = 1;
});
If you run it with mocha --check-leaks you'll get an error because the test creates a new foo global. You can prevent the error with mocha --check-leaks --globals foo. In large projects there are perhaps leaks that are deemed to be okay and so using the --globals option allows turning off errors for those cases that are okay.
Now, how can you achieve what you wanted to do? You cannot create a global variable if you use var. Your a.js would have to be:
user = 'some user';
Moreover, by default Mocha does not guarantee an order when it loads test files. You could use --sort to guarantee that a.js is loaded first but you are then forced to use a name that will guarantee this order. I prefer to use --require a which tells Mocha to require module a before it starts reading the test files. Your module could be called z and would still be loaded before any test file. At any rate, by removing var and using --require your test files will see your global. (I tried it: it works.)

This seemed to work for me using sailsjs (I think there would be a better way to do this), one of the first test that I run is to register a user:
describe('#Should register test user', function () {
it('should register user', function (done) {
request(sails.hooks.http.app)
.post('/auth/registertest')
.send({
"email": "email#foo.com.au",
"password": "foo",
"firstname": "foo",
"lastname": "foo",
"location": {
"name": "foo",
"id": "54d733795ed3f5140b0a761b"
}
})
.expect(200).end(function (err, res) {
if (err) return done(err);
global.email = "email#tpg.com.au";
global.token = res.body.token;
done();
})
});
});
Then I am able to access the global email and token variables in other test files that run after the register user test, for example the below calls my api and uses the global token variable in the Authorization header.
describe('#auth access to users bunches', function () {
it('should get 200', function (done) {
request(sails.hooks.http.app)
.get('/bunch/byuser')
.set('Authorization', 'bearer ' + token)
.expect(200).end(function (err, res) {
if (err) return done(err);
done();
})
});
});

Related

node.js - Capture debug output from module

I need to capture debug information while I make a require call to find out why some packages are not found.
In Module (internal module.js) there are several debug calls such as:
if (parent) {
debug('looking for %j in %j', id, paths);
}
What do I need to do to capture this debug information?
Thanks
debug() is a function that was created using util.debuglog(), which means that if you set the correct environment variable, the debug messages will be written to stderr, which you can then capture to a file (for instance):
env NODE_DEBUG=module node your-app.js 2> debug.log
EDIT: to capture these messages from within your own app, I think you have to resort to monkeypatching, for instance console.error()
let error = console.error.bind(console);
console.error = function() {
if (arguments[1] !== 'MODULE') {
return error.apply(this, arguments);
}
console.log('error log', arguments);
};
This code needs to run before any of the require() statements that you want to track, and it's not very robust (if the implementation of util.debuglog ever changes, it might break).

Sinon stub check that external SOAP call was called with specific arg

I'm using Jasmine and sinon to test some node.js code. I'm using sinon stubs in this example.
I am trying to stub out a SOAP call to an external service, and I'm wondering if there's any way to ensure that the correct argument is being used in the call.
I have successfully checked if functions are returning the correct arguments in other circumstances, but unfortunately, this scenario is within a nested callback, so I'm not sure how to do it.
Here's my code snippet (I'm trying to test that "client.ExternalService.HttpPort.actualCall" is being called with the "args" variable I am expecting):
class ExternalServiceCaller extends BaseService {
constructor(util) {
super(util);
this.util = util;
}
callExternalService(body, callback){
let url = this.util.config.get('blah.my.url');
let args = {
'request':{
'Property1': body.Property1,
'Property2': body.Property2,
'Property3': body.Property3,
'Property4': body.Property4
}
};
//soap.request()
soap.createClient(url, sslOptions, function(err, client) {
//client.[wsdlName].[binding name].[operation]
client.ExternalService
.HttpPort
.actualCall(args, function(err, result) {
if(!err){
callback(null, result);
}
}, sslOptions);
});
}
}
As I said above, I'm trying to write a test to make sure that actualCall is using the expected "args" variable (making sure that the incoming body is being formatted correctly to be passed to the external call). I can do this pretty easily for the url by stubbing out soap.createClient and using sinon.assert.calledWith() like below:
describe('The function', function(){
let service;
let externalServiceStub;
let externalRequest = helper.myExternalRequestObject;
describe('should use the correct URL',function(){
beforeEach(function(){
service = new ExternalServiceCaller(tools);
externalServiceStub = sinon.stub(soap, 'createClient');
});
it ('and uses the correct URL when successful', function(){
let url = tools.config.get('blah.my.url');
service.callExternalService(myExternalRequestObject, callback => {});
sinon.assert.calledWith(externalServiceStub, url);
externalServiceStub.restore();
});
});
Unfortunately, I have no idea how to check to see that actualCall is being called with the "args" variable that I'm expecting. I could use a fake object to check it against, but I'm not sure how exactly to do that check in the first place in this scenario.
I looked into soap stub, but there isn't a whole lot of documentation and the example didn't make sense to me.
Any help would be greatly appreciated. :)

Uglify the global variable

I have an app in nodejs. In it, I define some global variables that are shared across multiple files. For example:
//common.js
async = requires("async");
isAuthenticated = function() {
//...
return false;
};
//run.js
require("common.js");
async.series([function () {
isAuthenicated();
}], function () {
console.log("done");
});
I want the async and isAuthenticated variables to be minified, but minified to the same thing in all files. It would look like the following:
//common.min.js
a = requires("async");
b = function() {
//...
return false;
};
//run.min.js
require("common.js");
a.series([function () {
b();
}], function () {
console.log("done");
});
How to do it in uglifyjs?
I'm currently looping through the files and using the command uglifyjs $file -m "sort,toplevel" -c > $file.min on each.
Don't use globals.
Use var async = reuqire('async') where needed.
Use module.exports in your specific modules you require.
Use something like browserify to generate a single js.
Uglify (or use a browserify transform named uglifyify)
For example, the simplest form (without using uglifyify)
$ browserify run.js | uglifyjs -c > run.min.js
Note that if you use your own code, like common.js, you should require it using a relative path, var common = require("./common").
I suggest you use the exports syntax:
// common.js code
exports.isAuthenticated = function() {
//...
return false;
};
And of course use it just as you would with async.js:
//run.js
var common = require("./common");
var async = require("async")
async.series([function () {
common.isAuthenicated();
}], function () {
console.log("done");
});
assuming both common.js & run.js reside in the same directory.
related question: How to get minified output with browserify?
A Side Note
The way you used async.series in your question has no real advantage. You could have just:
//run.js
var common = require("./common");
common.isAuthenicated();
console.log("done");
in Async series you usually call async functions:
async.series([
function(callback){
// do some stuff ...
callback(null, 'one');
},
function(callback){
// do some more stuff ...
callback(null, 'two');
}
],
// optional callback
function(err, results){
// results is now equal to ['one', 'two']
});
so, I would expect to see something like:
// common.js code
exports.isAuthenticated = function(callback) {
//...
callback(null, false);
};
and then
//run.js
var common = require("./common");
var async = require("async")
async.series([common.isAuthenicated], function (err, results) {
console.log("done with", results[0]);
});
I usually prefer a different "syntax"
// an example using an object instead of an array
async.series({
one: function(callback){
setTimeout(function(){
callback(null, 1);
}, 200);
},
two: function(callback){
setTimeout(function(){
callback(null, 2);
}, 100);
}
},
function(err, results) {
// results is now equal to: {one: 1, two: 2}
});
But it's your call.
The async examples were taken from https://github.com/caolan/async#seriestasks-callback
You would want to concat the files before you go ahead and uglify them. Concatenation is the process of combining multiple files of code into one monolithic creature that knows everything about all parts of your code. This is often done in conjunction with uglyfying for several reasons, mainly for performance benefits (your app runs a lot faster if you only send 1 file to the client).
That being said, this is typically a practice that is done when your serving code to a client, not necessarily for back-end / server-side logic. Ideally no one but you or people with access to whatever service you're using to deploy said server code should see that side of your code. If your main concern is to prevent reverse-engineering, or make your code unreadable, I suggest obfuscating your code.
"This is omega site. Best encrypted level he has. Looks like obfuscated code to conceal its true purpose. Security through obscurity." - Q Skyfall 2012
If your globals are confined to common.js, you may try
uglifyjs --define-from-module common.js $file...
and remove require()s.
In NodeJs there is the concept of defining global variables like posted in this thread:
global.myGlobalVar = "something visible to all modules";
I am too using uglify in my node apps, and it turned out that when using global.xyz, xyz does not get uglified.
disclaimer: I am totally aware that exposing global info is an anti pattern. But sometimes there is a good reason for it.
Hope that helps!

Assumption: Is it true that Meteor.call() tries to run in the client environment first?

Experimenting with Meteor due to this question, I came to the following conclusion:
Defined in a shared directory (client/server), this will throw an Reference Error:
if(Meteor.isServer) {
// could depend on server logic, this is not Meteor.isServer!
serverVar = true;
}
Meteor.methods({
myMethod: function() {
if(serverVar) {
return "secret";
} else {
throw Error();
}
}
}
Then, on the client:
Meteor.call("myMethod", function(err, res) {
console.log(res);
}
Leads to: ReferenceError: serverVar
But this code, being defined on the server side only, runs flawlessly:
// could depend on server logic, this is not Meteor.isServer!
serverVar = true;
Meteor.methods({
myMethod: function() {
if(serverVar) {
return "secret";
} else {
throw Error();
}
}
}
Note that I only switched to a server-side directory instead of a shared one and removed the if-clause.
I thought that both approaches should be equivalent, ignoring the fact that code is visible
on the client when only limited by Meteor.isServer.
This leads me to the conclusion that Meteor, using the first approach, tries to run code on the client when it is not explicitly limited to the server. Is that true?! What could be another explanation?
dont use Meteor.isServer()
Meteor.isServer can be used to limit where code runs, but it does not
prevent code from being sent to the client.
check this answer to structure your meteor app
Okay, I got it. Using the first approach, Meteor throws a ReferenceError. This is due to the client-side simulation of the function. This feature is described in the documentation here.
So the code works with both approaches, but when also being defined on the client, it throws that ReferenceError. This does not happen anymore when limiting the scope to the server.
I think you might just need a var serverVar; at the very top (shared)

unit testing in javascript: how do you mock? - a (hard for me) example

I just rewrote backbone-mongodb to be really compatible with backbone. The original solution had nice vows for testing, and I would like my code to get tested as well, but simply have no idea how to do it.
Here is an example, I would like to test:
update: function(callback) {
var model = this.model;
this._withCollection(function(err, collection) {
if (err) callback(err);
else {
var attributes = _.clone(model.attributes);
delete attributes['_id'];
collection.update({ _id: new ObjectID(model.id) }, {$set: attributes}, {safe:true, upsert:false}, function(err) {
model.fetch();
callback(null, model.toJSON());
});
}
});
},
This code has nothing special in it. It uses the node-mongodb-native driver, and updates a record in the database. AFAIK, proper testing would mean to at least check that (1) collection.update was called with the given arguments, (2) callback is called when and how it should be, (3) model contains the new data.
With vows I can check (2), but have no idea at all how to check (1). Actually, the same holds for every unit testing framework I know about, qUnit, Jasmine. I'm sure that this can be done somehow, and I'm decided to learn at least one of them, but it's hard to make a choice when you got stuck at the beginning. :)
I know about sinon.js and think that everyting can be tested using mocking all the objects I have until I end up having the collection mocked as well, but this seems to be extremely clumsy. Could someone help me in writing the above tests please, and I'll happy to write out a tutorial of it?
I will use Jasmine for that purpose; I don't how familiar are you using that library, but they have a plugin to use jQuery for writing spec tests, you can load fixtures/templates and run tests on it.
for your particular case, assuming that function is part of MyObj "class", I will write something like:
describe("My object tests", function() {
it("Should update my obj", function () {
var noError, flag = false;
MyObj.update(function (err, model){
flag=true;
noError= err==null;
// or you can do other checks on the result
})
// we wait for 5 sec until get a response (flag==true)
waitsFor(function (){ return flag}, "Timeout on update", 5000);
// check if there are no errors
expect(noError).toBeTruthy();
});
});

Categories