2

I'm trying to improve test coverage on this file

'use strict'

/**
 * Returns the logging options we're using
 *
 * @param   {String} name Name of the logger. Should be service name. e.g. ciitizen-file-service
 * @returns {Object}      The logging options we're using
 * @private
 */
function getLoggerOptions(name) {
  return {
    name: name,
    streams: [
      {
        level: 'info',
        stream: stdOutStream()
      }, {
        level: 'error',
        stream: stdErrStream()
      }
      , {
        level: 'debug',
        stream: stdOutStream()
      },
      {
        level: 'warn',
        stream: stdOutStream()
      },
      {
        level: 'fatal',
        stream: stdErrStream()
      }
    ]
  }
}

/**
 * Creates a stream that writes to stdout
 *
 * @param   {Object} info The log info
 * @returns {Object}      An object which logs to stdout
 * @private
 */
function stdOutStream() {
  return {
    write: log => {
      // Change log level number to name and write it out
      log.level = bunyan.nameFromLevel[log.level]
      process.stdout.write(JSON.stringify(log) + '\n')
    }
  }
}

/**
 * Creates a stream that writes to stderr
 *
 * @param   {Object} info The log info
 * @returns {Object}      An object which logs to stderr
 * @private
 */
function stdErrStream() {
  return {
    write: log => {
      // Change log level number to name and write it out
      log.level = bunyan.nameFromLevel[log.level]
      process.stderr.write(JSON.stringify(log) + '\n')
    }
  }
}

module.exports = { getLoggerOptions }

I currently have this test

'use strict'

const expect = require('chai').expect
const { getLoggerOptions } = require('../src/helpers')

describe.only('#getLoggerOptions', () => {
  it('should return the logger options', () => {
    expect(getLoggerOptions('test')).to.be.an('object')
  })
})

Which produces this gap in unit test coverage enter image description here

How can i write a test to cover this code?

Catfish
  • 18,876
  • 54
  • 209
  • 353
  • Try to use stubs (`sinon.stub`) to test such functionality. Create stub and test that stubbed function was called. – alexmac Dec 07 '18 at 23:20

1 Answers1

1

You are only asserting that getLoggerOptions(...) should returns an object, but you are not accessing the object.

For instance, apart from the posted test, try adding a test like:

describe.only('#probeLoggerOptions', () => {
  it('should do expected logging stuff', () => {
    const {name, streams} = getLoggerOptions('test');
    for (let stream of streams) {
      console.log(`getting logopt for level ${stream.level}`);
      expect(stream.stream.write(log), /* ... what to expect */);
    }
  });
});

Now the test is calling the write function in each object.

EXTRA: If you own the code, the best way is to refactor it to be more testable. For instance, instead of writing to high-level process streams directly, you might create a WritableStream, write to it, then pipe to the desired process stream.

function configureStream(writable = process.stdout, data = null, msg = 'done') {
  const stream = fs.createReadStream('/tmp/log');
  stream.read(data);
  stream.pipe(writable);

}

function stdOutStream() {
  return {
    write: log => {
      // Change log level number to name and write it out
      log.level = bunyan.nameFromLevel[log.level];
      configureStream(data = JSON.stringify(log) + '\n');
    }
  }
}

Apart from being able to test configureStream separately, you could also abstract the meat of each logging process to one function that reduce the test surface. Now you could also hook into stream's events like 'pipe' or 'finish' to check the content being logged.

Pandemonium
  • 7,724
  • 3
  • 32
  • 51
  • 1
    Your first example fails b/c log is undefined `expect(stream.write(log)` – Catfish Dec 07 '18 at 18:29
  • When i change it to `expect(stream.write('foo'))` I now get the error `TypeError: stream.write is not a function` – Catfish Dec 07 '18 at 18:30
  • @Catfish `log` was not provided because I had no knowledge of it. You should be able to provide that yourself. – Pandemonium Dec 07 '18 at 18:45
  • 1
    Excuse me, the `stream` here was an object with properties `level` and `stream`, thus you have to call `stream.stream.write(log)`. I corrected it. – Pandemonium Dec 07 '18 at 18:52
  • I"m trying to follow your "extra", but your code doesn't work. You can't create a write stream like that. You need to pass a string or a buffer. I changed it to ` const myStream = fs.createWriteStream('foobar') myStream.write('foo') myStream.pipe(myStream) myStream.end()` and I now get the error `Error [ERR_STREAM_CANNOT_PIPE]: Cannot pipe, not readable` – Catfish Dec 07 '18 at 20:14
  • @Catfish I apologize but I didn't really have access to Nodejs to test out the EXTRA bit. Apart from EXTRA did the main question get answered though? – Pandemonium Dec 08 '18 at 01:44
  • Yep it sure did – Catfish Dec 10 '18 at 22:34