Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit test console output with mocha on nodejs?

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?

like image 774
Kemel Zaidan Avatar asked Jun 03 '15 16:06

Kemel Zaidan


People also ask

How do you test a single mocha test?

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', _ => { // ... })


2 Answers

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);   }); }); 
like image 172
robertklep Avatar answered Sep 21 '22 02:09

robertklep


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);   }); }); 
like image 36
Eliran Malka Avatar answered Sep 21 '22 02:09

Eliran Malka