Object state leaking between Mocha tests - node.js

I'm new to Mocha and I'm hoping someone can help me. I'm curious how I can ensure that actions performed in one test do not effect another. For example, assume that I have a function microbe that returns an object that looks like:
{
_state : {
views : 'views'
}
set : function(key, val) { this._state[key] = val }
}
This is a majorly reduced version of what I'm working with, but it demonstrates what I'm testing, using a test suite such as:
var expect = require('chai').expect;
describe('State', function() {
var microbe = require('../microbe/index'), app;
beforeEach(function(done) {
app = microbe();
});
it('should update the view location with app.set', function() {
app.set('views', 'test');
expect(app._state.views).to.equal('test');
});
it('should return "views" as the defualt view location', function() {
expect(app._state.views).to.equal('views');
});
});
The second test fails because the first test sets app._state.views to 'test' so it overrides the default of 'views'. I thought Mocha would isolate each test, but apparently not. I've tried to just re-instantiate the app for each test as shown usingbeforeEach, but it still seems to be leaky.
Whats the best way to do this? I know I could just reverse the order of the tests and it would technically pass, but I'd rather fix the issue itself than just avoid it

It looks as if your function microbe() is behaving as a singleton.
The easiest way of having a fresh instance of your microbe is to use the new keyword within the beforeEach():
beforeEach(function(done) {
app = new microbe();
});
Doing this, each execution of a test will start freshly with a 'default' instance of the object you are returning.

I just tested the code you posted and the tests run correctly:
test.js:
var expect = require('chai').expect;
describe('State', function() {
var microbe = require('./microbe.js'), app;
beforeEach(function() {
app = microbe();
});
it('should update the view location with app.set', function() {
app.set('views', 'test');
expect(app._state.views).to.equal('test');
});
it('should return "views" as the defualt view location', function() {
expect(app._state.views).to.equal('views');
});
});
microbe.js:
module.exports = function () {
return {
_state : {
views : 'views'
},
set : function(key, val) { this._state[key] = val; }
};
};
Executing mocha outputs:
/tmp mocha -R spec test.js
State
✓ should update the view location with app.set
✓ should return "views" as the defualt view location
2 passing (5ms)
You have something strange in your implementation (maybe a singleton or a global variable somewhere) but it's not Mocha's fault.

Related

Using nock to mimic multiple couch db requests

In my original function I need to make 2 requests to 2 different db's within the same couch login.
var cloudant = require('cloudant')(https://cloudant_url);
var userdb = cloudant.db.use('user');
var addrdb = cloudant.db.use('address');
function onChange(username) {
userdb.get(username, function(err,resp) {
var user_id = resp.id;
addrdb.get(user_id,function(err1,resp1){
var addr = resp1.address;
});
});
};
var nockVar = function() {
nock(testCloudantDBURL)
.get('/user/jack')
.reply(200,{'id' : 123});
nock(testCloudantDBURL)
.get('/address/123')
.reply(200,{'address':'123});
};
describe('Test Cloudant Listener code' , function() {
nockVar();
it('test get scenario', function() {
onChange('jack');
});
});
With this only the first call works and I can get the id : 123. The second call on address db is not getting intercepeted.
With nock I'm able to intercept only the first call,the second call is not happening.Any pointers ?
This happens because your code is executed asynchronously and your test doesn't wait for the userdb.get and addrdb.get to finish. Easiest (not best) way to handle this is to add a done callback to your test scenario and call it as soon as your onChange function is finished. Roughly something like:
function onChange(username, done) {
userdb.get(username, function(err,resp) {
var user_id = resp.id;
addrdb.get(user_id,function(err1,resp1){
var addr = resp1.address;
done();
});
};
};
it('test get scenario', function(done) {
onChange('jack', done);
});
You might also consider working with Promises based code.

Loopback testing with supertest, mocha and models

On the Google groups post on deprecating loopback-testing there is a question that asks about providing a proper example of how testing can be achieved without loopback-testing. That thread talks about using supertest instead.
Below is an attempt that I made to combine Mocha, supertest along with models (from app.js). The result works really well when I run the file by itself. But if I had another test file (say test-teacher.js) then the first test file (call it test-student.js) starts to fail in weird ways I can't describe.
Am I missing something or can models not be used like I am using them below?
describe('/Student', function () {
var server = require('../server/server')
var loopback = require('loopback')
var supertest = require('supertest')
var request = require('supertest')(server)
var dataSource = server.dataSource('db', {adapter: 'memory'})
var Student = dataSource.define('Student', {
'id': Number,
'points': Number
});
beforeEach(function () {
Student.updateOrCreate({id: 1, points: 5000});
})
it('Post a new student', function (done) {
request.post('/api/Students').send({points: 5000}).expect(200, done)
})
})
Based on feedback from jakerella on the previous answer, I changed the code above so that I don't have to redefine the models from scratch in the code (thanks jakerella!)
With the code below, I am able to run all the tests from multiple different models as a single suite using npm test without any failures.
Since I am interested in only individual orders ... listen and close were not necessary. I suspect that if I was testing overall instances of models that were created it would become required.
describe('/Student', function () {
var server = require('../server/server')
var request = require('supertest')(server)
var expect = require('expect.js')
var Student
before(function() {
Student = server.models.Student
})
beforeEach(function (done) {
Student.upsert({id: 1, points: 5000}, function() { done() })
})
it('Post a new student', function (done) {
request.post('/api/Students').send({points: 5000}).expect(200, done)
})
})
Wanted to toss this into an answer... the first issue was an undefined dataSource var, but then you also had redefined Student in your two tests. My recommendation instead is used the LoopBack app and models already defined (usually in common/models/).Then the basic implementation for testing (that I use) is something like the code below (using mocha and chai). Note the beforeEach and afterEach to start and stop the server.
var assert = require('chai').assert,
superagent = require('superagent'),
app = require('../server/server');
describe('Person model', function() {
var server;
beforeEach(function(done) {
server = app.listen(done);
});
afterEach(function(done) {
server.close(done);
});
it('should log in and log out with live server', function(done) {
superagent
.post('http://localhost:3000/api/People/login')
.send({ email: 'john#doe.com', password: 'foobar' })
.set('Accept', 'application/json')
.set('Content-Type', 'application/json')
.end(function(err, loginRes) {
if (err) { return done(err); }
assert.equal(loginRes.status, 200);
assert.ok(loginRes.body);
assert.equal(loginRes.body.userId, 1);
}
});
});
});

provide data for protractor test

I'm new to nodejs and working now to automatize functionnal test doing BDD with cucumber and protractor.
For some of my steps, i need to send query to an oracle database and then use the result to make a search in the tested website.
1- I'm trying with oracleDB witch returns me the expected result that i put in a variable but this one is not available in my steps. Method sendKeys of webdriver puts "undefined" in the input.
2- I'm also wondering if there's not another way because it was hard to install oracledb and the next task is to have jenkins builds
here is my code:
var dbQueryContract = function() {
var oracledb = require('oracledb');
//Database communication
oracledb.getConnection(
{
user : "xxx",
password : "xxx",
connectString : "xxx"
},
function(err, connection)
{
if (err) {
console.error(err.message);
return;
}
connection.execute(
"select xxx, xxx " +
"FROM xxxxx " +
"where xxx is not null and rownum < 5",
{
resultSet: true
},
// bind value for :id [110],
function(err, result)
{
if (err) { console.error(err.message); return; }
console.log(result.rows[0][0]);
});
criteria= result.rows[0][0];
// the connection is ok and i can log result.rows[0][0] i want to use for search
});
};
module.exports = new dbQueryContract();
************************************************************************************************
// Use the external Chai As Promised to deal with resolving promises in
// expectations.
var chai = require('chai');
var chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
var expect = chai.expect;
var page1 = require('../page1.js');
var page2 = require('../page2.js');
var dbQueryContract = require('../dbQueryContract.js');
var EC = protractor.ExpectedConditions;
// Chai expect().to.exist syntax makes default jshint unhappy.
// jshint expr:true
module.exports = function() {
this.Given(/^thanx for help$/, function(next) {
browser.ignoreSynchronization=true;
browser.get('toto.com');
page1.login.sendKeys('login')
page1.password.sendKeys('P#ssword')
page1.validateButton.click();
browser.ignoreSynchronization=false;
page2.searchLink.click();
browser.waitForAngular();
browser.sleep(5000);
// console.log(dbQueryContract.numabo.result.rows[0][0]);
dbQueryContract().then(function(criteria) {
page2.searchInput.sendKeys(criteria, protractor.Key.ENTER);
});
next();
});
this.When(/^i learn more$/, function(next) {
browser.sleep(5000);
next();
});
};
This code doesn't look right. Your module is exporting the result of invoking new dbQueryContract();. However, that function isn't really a constructor function. You're not setting any properties on this or adding any properties to the prototype. Also, you're not returning anything, which is probably why your getting undefined later.
The next problem is that you're chaining a then call, assuming that the driver uses promises - it doesn't (at least not yet). You'd need to have your function return a deferred which you'd need to resolve at the right time yourself. There's some discussion here about adding more robust JavaScript layer:
https://github.com/oracle/node-oracledb/pull/321
If we go that route I'd like to see all async methods return a promise by default which would make things easier...

How can I test through a mocked promise with Mocha?

My node.js code is:
function getPatientNotificationNumbers(patientId) {
patientId = patientId && patientId.toString();
var sql = "SELECT * FROM [notification_phone_number] ";
sql += "WHERE patient_id = " + escapeSql(patientId);
return sqlServer.query(sql).then(function(results) {
var phoneNumbers = _.map(results[0], function (result) {
var obj = {
id: result.id,
phoneNumber: result.phone_number,
isPrimary: result.is_primary,
isVerified: result.is_verified,
patientId: result.patient_id
}
return obj;
});
return phoneNumbers;
});
}
Pretty simple and straight forward. What I want to test is that the return of this function, properly resolved, is an array of phoneNumbers that match that format.
sqlServer is require'd above and I have a ton of things require'd in this file. To stub them out, I am using mockery, which seems to be pretty great.
Here is my test, so far:
before(function() {
deferred = Q.defer();
mockery.enable();
moduleConfig.init();
mockery.registerSubstitute('moment', moment);
mockery.registerAllowable('../../util');
mockStubs.sqlServer = {
query: sinon.stub().returns(deferred.promise)
}
mockery.registerMock('../../db/sqlserver', mockStubs.sqlServer);
methods = require('../../../rpc/patient/methods');
});
beforeEach(function() {
deferred = Q.defer();
})
it('should get the patient notification numbers', function(done) {
// sinon.spy(sqlServer, 'query').and.returnValue(deferred.promise);
deferred.resolve('here we go');
methods.getPatientNotificationNumbers(1).then(function(result) {
console.log(result);
done();
});
});
However, it never gets past sqlServer.query in my code. So the results are pointless. I also tried something like:
response = methods.getPatientNotificationNumbers(1)
but when I console.log(response), it's basically {state: 'pending'}, which I guess is an unresolved promise.
So I'm all over the place and I'm open to using whatever libraries make things easy. I am not married to mockery, sinon or whatever else. Any suggestions would help.
Thanks!
So another approach is to use a combination of rewire and deride.
var should = require('should-promised');
var rewire = require('rewire');
var Somefile = rewire('./path/to/file');
var deride = require('deride');
var sut, mockSql;
before(function() {
mockSql = deride.stub(['query']);
Somefile.__set__('sqlServer', mockSql);
sut = new Somefile();
});
describe('getting patient notification numbers', function() {
beforeEach(function() {
mockSql.setup.query.toResolveWith(fakeSqlResponse);
});
it('resolves the promise', function() {
sut.getPatientNotificationNumbers(id).should.be.fulfilledWith(expectedPhoneNumbers);
});
it('queries Sql', function(done) {
sut.getPatientNotificationNumbers(id).then(function() {
mockSql.expect.query.called.once();
done();
});
});
});
This will mean that you do not need to change your production code and you can easily start testing the un-happy paths using something like this:
it('handles Sql errors', function(done) {
mockSql.setup.query.toRejectWith(new Error('Booom'));
sut.getPatientNotificationNumbers(id)
.then(function(){
done('should not have resolved');
})
.catch(function(e) {
e.should.be.an.Error;
e.message.should.eql('Booom');
done();
});
});
Or even more succinctly:
it('handles Sql errors', function(done) {
mockSql.setup.query.toRejectWith(new Error('Booom'));
sut.getPatientNotificationNumbers(id).should.be.rejectedWith(/Booom/);
});
I would zoom out a little bit and think of the test strategy. Assume that the goal is to test your model layer (methods) on top of the sql server. It can be done without stubbing out sql server. Your test suite layer can have set of util methods to create, initialize and drop the db which can be called from before, beforeEach etc.
Pros of doing this:
Testing real product code path is better (than stubbed code path).
Stubbing is better if you suspect the underlying layer bugs to generate noise. sqlserver layer is likely stable.
model layer seem to be simple enough that does not require testing in isolation.
Stubbing would make sense if you are trying to test sqlserver failure handling in your model layer. Then the stub layer can fake such errors - to exercise error paths in model code.
This is based on limited view of your problem. If there is more to it. Pl do share and we can take it from there.
It seems to me your intent is to test the function your passing the the .then() method, not the sqlserver library nor the actual promise it returns. We can assume those have already been tested.
If this is the case, you can simply factor out that function (the one that extracts the phone numbers from the SQL result) and test it independently.
Your code would become something along the lines of:
var getNumbers = require('./getNumbers');
function getPatientNotificationNumbers(patientId) {
patientId = patientId && patientId.toString();
var sql = "SELECT * FROM [notification_phone_number] ";
sql += "WHERE patient_id = " + escapeSql(patientId);
return sqlServer.query(sql).then(getNumbers);
}
... and your ./getNumbers.js file would look something like:
function getNumbers(results) {
var phoneNumbers = _.map(results[0], function (result) {
var obj = {
id: result.id,
phoneNumber: result.phone_number,
isPrimary: result.is_primary,
isVerified: result.is_verified,
patientId: result.patient_id
}
return obj;
});
return phoneNumbers;
}
module.exports = getNumbers;
Now you can test it independently:
var getNumbers = require('../path/to/getNumbers');
...
it('should get the patient notification numbers', function(done) {
var sampleResults = [ ... ]; // sample results from sqlServer
var phoneNumbers = getNumbers(sampleResults);
assert(...); // make what ever assertions you want
});

Mocha Monitor Application Output

I'm building a logging module for my web app in nodejs. I'd like to be able to test using mocha that my module outputs the correct messages to the terminal. I have been looking around but haven't found any obvious solutions to check this. I have found
process.stdout.on('data', function (){})
but haven't been able to get this to work. does anybody have any advice?
process.stdout is never going to emit 'data' events because it's not a readable stream. You can read all about that in the node stream documentation, if you're curious.
As far as I know, the simplest way to hook or capture process.stdout or process.stderr is to replace process.stdout.write with a function that does what you want. Super hacky, I know, but in a testing scenario you can use before and after hooks to make sure it gets unhooked, so it's more or less harmless. Since it writes to the underlying stream anyway, it's not the end of the world if you don't unhook it anyway.
function captureStream(stream){
var oldWrite = stream.write;
var buf = '';
stream.write = function(chunk, encoding, callback){
buf += chunk.toString(); // chunk is a String or Buffer
oldWrite.apply(stream, arguments);
}
return {
unhook: function unhook(){
stream.write = oldWrite;
},
captured: function(){
return buf;
}
};
}
You can use it in mocha tests like this:
describe('console.log', function(){
var hook;
beforeEach(function(){
hook = captureStream(process.stdout);
});
afterEach(function(){
hook.unhook();
});
it('prints the argument', function(){
console.log('hi');
assert.equal(hook.captured(),'hi\n');
});
});
Here's a caveat: mocha reporters print to the standard output. They do not, as far as I know, do so while example (it('...',function(){})) functions are running, but you may run into trouble if your example functions are asynchronous. I'll see if I can find more out about this.
I've attempted jjm's answer and had problems which I suspect was due to my programs async behaviour.
I found a solution via a cli on github that uses the sinon library.
An example code to test:
/* jshint node:true */
module.exports = Test1;
function Test1(options) {
options = options || {};
}
Test1.prototype.executeSync = function() {
console.log("ABC");
console.log("123");
console.log("CBA");
console.log("321");
};
Test1.prototype.executeASync = function(time, callback) {
setTimeout(function() {
console.log("ABC");
console.log("123");
console.log("CBA");
console.log("321");
callback();
}, time);
};
And the mocha tests:
/* jshint node:true */
/* global describe:true, it:true, beforeEach:true, afterEach:true, expect:true */
var assert = require('chai').assert;
var expect = require('chai').expect;
var sinon = require("sinon");
var Test1 = require("../test");
var test1 = null;
describe("test1", function() {
beforeEach(function() {
sinon.stub(console, "log").returns(void 0);
sinon.stub(console, "error").returns(void 0);
test1 = new Test1();
});
afterEach(function() {
console.log.restore();
console.error.restore();
});
describe("executeSync", function() {
it("should output correctly", function() {
test1.executeSync();
assert.isTrue(console.log.called, "log should have been called.");
assert.equal(console.log.callCount, 4);
assert.isFalse(console.log.calledOnce);
expect(console.log.getCall(0).args[0]).to.equal("ABC");
expect(console.log.getCall(1).args[0]).to.equal("123");
expect(console.log.args[2][0]).to.equal("CBA");
expect(console.log.args[3][0]).to.equal("321");
});
});
describe("executeASync", function() {
it("should output correctly", function(done) {
test1.executeASync(100, function() {
assert.isTrue(console.log.called, "log should have been called.");
assert.equal(console.log.callCount, 4);
assert.isFalse(console.log.calledOnce);
expect(console.log.getCall(0).args[0]).to.equal("ABC");
expect(console.log.getCall(1).args[0]).to.equal("123");
expect(console.log.args[2][0]).to.equal("CBA");
expect(console.log.args[3][0]).to.equal("321");
done();
});
});
});
});
I'm providing the above as it demonstrates working with async calls, it deals with both console and error output and the method of inspection is of more use.
I should note that I've provided two methods of obtaining what was passed to the console, console.log.getCall(0).args[0] and console.log.args[0][0]. The first param is the line written to the console. Feel free to use what you think is appropriate.
Two other libraries that help with this are test-console and intercept-stdout I haven't used intercept-stdout, but here's how you can do it with test-console.
var myAsync = require('my-async');
var stdout = require('test-console').stdout;
describe('myAsync', function() {
it('outputs something', function(done) {
var inspect = stdout.inspect();
myAsync().then(function() {
inspect.restore();
assert.ok(inspect.output.length > 0);
done();
});
});
});
Note: You must use Mocha's async api. No calling done() will swallow mocha's test messaging.

Resources