Q:

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?

A:

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);
  });
});

A:

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);
  });
});

