node.jsjestjswinston

How to write unit test for winston logger in Nodejs


I am writing a logging framework in Nodejs using winston logger. Now I have to write unit test to test my class using jest.

Below is my LoggerClass:

'use strict';

const { createLogger, format, transports } = require('winston');

const fs = require('fs');
const env = process.env.NODE_ENV || 'development';

const logger = createLogger({
    level: env === 'development' ? 'debug' : 'info',
    format: format.combine(
        format.timestamp({
            format: 'YYYY-MM-DD HH:mm:ss'
        }),
        format.printf(info => `${info.timestamp} ${info.level}: ${info.message}`)),
    transports: [
        new transports.Console({
            level: 'info',
            format: format.combine(
                format.printf(
                    info => `${info.level}: ${info.message}`
                )
            )
        }),
        new transports.File({ filename })
    ]
});

module.exports = logger;

I have written the test as:

const logger = require('./WinstonLogger');

global.console = {
    log: jest.fn(),
    info: jest.fn(),
    error: jest.fn(),
}

describe('Tests my console.log', () => {
    it('should console a message', () => {
        logger.info('Hello world');
        expect(global.console.log).toHaveBeenCalledWith(
            'info: Hello world'
        )
    })

})

How can i modify my test?


Solution

  • Here is the unit test solution:

    logger.js:

    'use strict';
    
    const { createLogger, format, transports } = require('winston');
    
    const env = process.env.NODE_ENV || 'development';
    const filename = 'filename';
    
    const logger = createLogger({
      level: env === 'development' ? 'debug' : 'info',
      format: format.combine(
        format.timestamp({
          format: 'YYYY-MM-DD HH:mm:ss',
        }),
        format.printf((info) => `${info.timestamp} ${info.level}: ${info.message}`),
      ),
      transports: [
        new transports.Console({
          level: 'info',
          format: format.combine(format.printf((info) => `${info.level}: ${info.message}`)),
        }),
        new transports.File({ filename }),
      ],
    });
    
    module.exports = logger;
    

    logger.test.js:

    jest.mock('winston', () => {
      const mFormat = {
        combine: jest.fn(),
        timestamp: jest.fn(),
        printf: jest.fn(),
      };
      const mTransports = {
        Console: jest.fn(),
        File: jest.fn(),
      };
      const mLogger = {
        info: jest.fn(),
      };
      return {
        format: mFormat,
        transports: mTransports,
        createLogger: jest.fn(() => mLogger),
      };
    });
    const { createLogger, format, transports } = require('winston');
    
    describe('logger', () => {
      afterEach(() => {
        jest.resetAllMocks();
      });
      it('should pass', () => {
        let templateFunctions = [];
        format.printf.mockImplementation((templateFn) => {
          templateFunctions.push(templateFn);
        });
        const logger = require('./logger');
        logger.info('Hello world');
        const info = {
          timestamp: 123,
          level: 'info',
          message: 'haha',
        };
        const tFn1 = templateFunctions.shift();
        expect(tFn1(info)).toBe(`${info.timestamp} ${info.level}: ${info.message}`);
        const tFn2 = templateFunctions.shift();
        expect(tFn2(info)).toBe(`${info.level}: ${info.message}`);
        expect(format.combine).toBeCalledTimes(2);
        expect(format.timestamp).toBeCalledWith({ format: 'YYYY-MM-DD HH:mm:ss' });
        expect(format.printf).toBeCalledWith(expect.any(Function));
        expect(transports.Console).toBeCalledTimes(1);
        expect(transports.File).toBeCalledWith({ filename: 'filename' });
        expect(createLogger).toBeCalledTimes(1);
      });
    });
    
    

    Unit test result with coverage report:

     PASS  src/stackoverflow/59388359/logger.test.js (10.484s)
      logger
        ✓ should pass (10ms)
    
    -----------|----------|----------|----------|----------|-------------------|
    File       |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
    -----------|----------|----------|----------|----------|-------------------|
    All files  |      100 |       50 |      100 |      100 |                   |
     logger.js |      100 |       50 |      100 |      100 |               5,9 |
    -----------|----------|----------|----------|----------|-------------------|
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   0 total
    Time:        11.789s
    

    Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59388359