Reputation: 613
Take into account the following example Javascript code below:
function privateFunction (time) {
if (time < 12) { console.log('Good morning'); }
if (time >= 12 && time <19) { console.log('Good afternoon'); }
else { console.log('Good night!'); }
};
How should I unit test that on nodejs using mocha (and possibly sinonjs), noticing that this is a private function called inside a module? I need to pass in the argument and check if the function is logging the right thing to the console.
Can I do the same with console.warn
and console.error
?
Upvotes: 49
Views: 64359
Reputation: 1037
If your purpose is solely to test console output, I would suggest instead of stubs / spies etc on method calls, to use something like:
monitor stdout/stderr instead, you can use ideas from this gist
Upvotes: 5
Reputation: 16263
ignoring the fact that it's a private function, i would take a couple of steps; refactor my code for better separation of concerns, and utilise this separation with test doubles.
take all the side effects outside to their own modules (the side effect here is writing to the console):
out.js
function log (message) {
console.log(message);
};
module.exports = {log};
app.js
const {log} = require('out');
function greeter (time) {
if (time < 12) {
log('Good morning');
}
if (time >= 12 && time < 19) {
log('Good afternoon');
} else {
log('Good night!');
}
};
module.exports = {greeter};
use some module proxy/spy, like proxyquire to replace the whole out writer when testing:
app.spec.js
describe('output writers', function(){
const fakeOut = {
log: sinon.spy(),
};
const app = proxyquire('./app', {
'out': fakeOut
});
it('should log to the fake out', function(){
app.greeter(15);
assert(fakeOut.log.calledOnce);
});
});
Upvotes: 7
Reputation: 203231
I prefer mocha-sinon
over "plain" sinon because it integrates nicely with Mocha.
Example:
var expect = require('chai').expect;
require('mocha-sinon');
// Function to test, can also be in another file and as long as it's
// being called through some public interface it should be testable.
// If it's not in any way exposed/exported, testing will be problematic.
function privateFunction (time) {
if (time < 12) { console.log('Good morning'); }
if (time >= 12 && time <19) { console.log('Good afternoon'); }
else { console.log('Good night!'); }
}
describe('privateFunction()', function() {
beforeEach(function() {
this.sinon.stub(console, 'log');
});
it('should log "Good morning" for hours < 12', function() {
privateFunction(5);
expect( console.log.calledOnce ).to.be.true;
expect( console.log.calledWith('Good morning') ).to.be.true;
});
it('should log "Good afternoon" for hours >= 12 and < 19', function() {
privateFunction(15);
expect( console.log.calledOnce ).to.be.true;
expect( console.log.calledWith('Good afternoon') ).to.be.true;
});
it('should log "Good night!" for hours >= 19', function() {
privateFunction(20);
expect( console.log.calledOnce ).to.be.true;
expect( console.log.calledWith('Good night!') ).to.be.true;
});
});
One potential issue: some Mocha reporters use console.log
as well, so the tests that stub it may not yield any output.
There's a workaround, but it's not ideal either because it will intersperse Mocha output with the output from privateFunction()
. If that's not a problem, replace beforeEach()
with this:
beforeEach(function() {
var log = console.log;
this.sinon.stub(console, 'log', function() {
return log.apply(log, arguments);
});
});
Upvotes: 50