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
?
Run a Single Test File Using the mocha cli, you can easily specify an exact or wildcarded pattern that you want to run. This is accomplished with the grep option when running the mocha command. The spec must have some describe or it that matches the grep pattern, as in: describe('api', _ => { // ... })
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); }); });
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); }); });
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With