使用带有默认printf

时间:2019-02-03 11:16:57

标签: javascript node.js jestjs

我已经使用winston,morgan和winston-daily-rotate-file,express-http-context实现了节点js的记录器。因此,每天,当任何http request(morgan)或开发人员定义的记录器消息写入该文件时,都会写入一个新的日志文件。从节点js。下面的格式将如何记录

时间戳||级别||文件名|| traceId || statusCode || logMessage

一切都很好,但是当我使用笑话(从初学到笑话)编写测试用例时。我无法覆盖两行。这是完整的代码和文件夹结构。

对于每个请求,我都设置一个traceId,该ID将在customFormat方法上提取,然后我返回customFormat消息,这是我无法讲的两行。

//index.js

const app = require('express')();
const cors = require('cors')
const morgan = require('morgan') // HTTP request logger middleware 
const logger = require('./lib/logger')(module) //Logger
const uuid = require('uuid')
const httpContext = require('express-http-context')
const config = require('./config').get(process.env.APP_ENV)


// Use any third party middleware that does not need access to the context here
// app.use(some3rdParty.middleware);

app.use(httpContext.middleware);
// all code from here on has access to the same context for each request

// Run the context for each request.
// Assigning a unique identifier to each request
app.use((req, res, next) => {
  httpContext.set('traceId', uuid.v1());
  next();
});

// using morgan with winston(logger)
app.use(morgan('combined', {
  stream: {
    write: (message) => logger[config.logLevel](message)
  }
}))

app.use(cors());

app.listen(4000, () => {
  console.log('Server running on port 4000');
});



// lib/logger/index.js

const appRoot = require('app-root-path');
const {
  createLogger,
  format,
  transports
} = require('winston');
require('winston-daily-rotate-file');
const {
  combine,
  label,
} = format;
const config = require('../../config').get(process.env.APP_ENV);
const loggerHelper = require('./helpers')



// Custom settings for each transport
const options = {
  dailyRotateFile: {
    filename: `${appRoot}/logs/TPS-UI-%DATE%.log`,
    datePattern: 'YYYY-MM-DD',
    prepend: true,
    level: config.logLevel,
    timestamp: new Date(),
    localTime: true,
  }
}

// Instantiate a Winston Logger with the settings
const logger = moduleObj => createLogger({
  format: combine(
    label({
      label: loggerHelper.getFileName(moduleObj)
    }),
    format.timestamp(),
    loggerHelper.customFormat()
  ),
  transports: [
    new transports.DailyRotateFile(options.dailyRotateFile),
  ],
  exitOnError: false, // do not exit on handled exceptions
});


module.exports = logger


// lib/logger/helpers/index.js

const loggerHelper = require('../../../helper');
const httpContext = require('express-http-context');
const {
  format: {
    printf
  }
} = require('winston')

/**
 * @method checkMessageProp
 * @param {message} can be object if developer defined, else it will be string
 *                  if its a network request (morgan requests)
 * @returns a fixed format how the status code and message should show
 */
const returnLogMessage = (message) => {
  const {
    statusCode,
    logMsg,
    maskedData
  } = message;
  switch (typeof message) {
    case 'object':
      let statusCodeToBeLogged = statusCode ? statusCode : "Status code not defined",
        logMessageToBeLogged = logMsg ? logMsg : "Log message not defined",
        return `${statusCodeToBeLogged} || ${logMessageToBeLogged}`
    case 'string':
      if (message) {
        const messageSplit = message.split('"');
        let statusCodeToBeLogged = messageSplit[2].trim().split(" ")[0],
          logMessageToBeLogged = messageSplit[1]
        return `${statusCodeToBeLogged} || ${logMessageToBeLogged}`;
      }
      return 'Status Code Not Defined || Log Message Not Defined';
    default:
      return message;
  }
};

/**
 * @method getFileName
 * @param {moduleObj} the module realted object passed from the require of logger file
 * @returns the file name where the logger was invoked
 */
const getFileName = (moduleObj) => {
  if (Object.keys(moduleObj).length > 0) {
    const tempFileNameParts = moduleObj.filename.split("/");
    const fileName = tempFileNameParts.slice(Math.max(tempFileNameParts.length - 2, 1)).join('/');
    return fileName;
  }
  return 'Module not passed while requiring the logger';
};

/**
 * @method customFormat
 * @param {log} the log passed by the developer or based on network requests
 * @returns a customFormat how it should be logged to the log files
 */

const customFormat = () => {
  return printf((log) => {
    const traceId = httpContext.get('traceId');
    return `${new Date(log.timestamp)} || ${log.level.toUpperCase()} || ${log.label} || ${traceId} || ${returnLogMessage(log.message)} `;
  })
}



module.exports = {
  returnLogMessage,
  getFileName,
  customFormat
}

// lib/logger/__test__/logger.test.js

jest.mock('winston');

const logger = require('..');
const winston = require('winston');

describe('Given the logger method is called', () => {
  let loggerObject;
  const mockModuleObject = {
    filename: 'server/index.js'
  };
  beforeEach(() => {
    loggerObject = logger(mockModuleObject);
  });
  test('it should return a object returned by createLogger', () => {
    expect(loggerObject).toEqual(winston.mockLoggerObject);
  });
  test('it should call combine, format, printf and timestamp method of winston', () => {
    expect(winston.mockPrintf).toHaveBeenCalled();
    expect(winston.mockLabel).toHaveBeenCalled();
    expect(winston.mockCombine).toHaveBeenCalled();
  });
  test('expect Dailytransports to be called', () => {
    // how to check the daily transport has been called
    expect(winston.mockDailyTransport).toHaveBeenCalled();
  });
});


// lib/logger/__test__/helpers/helper.test.js

jest.mock('winston')
jest.mock('express-http-context')
const helper = require('../../helpers/')
const httpContext = require('express-http-context')
const {
  format: {
    printf
  }
} = jest.requireActual('winston')

describe('Given the helper methods for logger should call a d take different scenarios', () => {
  let mockMessageObj, mockMessageString, mockMessageStringEmpty, mockMessageNumber;
  beforeAll(() => {
    mockMessageObj = {
      statusCode: 200,
      logMsg: "Testing log"
    }
    mockMessageString = `::1 - - [31/Jan/2019:11:26:54 +0000] 
            "GET /graphql HTTP/1.1" 404 146 "-" "Mozilla/5.0 (X11; Linux x86_64)
             AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36"`
    mockMessageStringEmpty = ""
    mockMessageNumber = 12345
  })
  test('returnLogMessage and getFileName methods should exist', () => {
    expect(helper.returnLogMessage).toBeDefined()
    expect(helper.getFileName).toBeDefined()
  })
  // returnLogMessage Method Test Cases
  test('should return a string when passes object', () => {
    expect(helper.returnLogMessage(mockMessageObj)).toEqual('200 || Testing log || userId : tne:1***:***2354')
  })
  test('should return a string when passes string', () => {
    expect(helper.returnLogMessage(mockMessageString)).toEqual('404 || GET /graphql HTTP/1.1')
  })
  test('should return default string when passes string as undefined', () => {
    expect(helper.returnLogMessage(mockMessageStringEmpty)).toEqual('Status Code Not Defined || Log Message Not Defined || Mask Data Not Defined')
  })
  test('should return the actual default message if the type is nor object neither string', () => {
    expect(helper.returnLogMessage(mockMessageNumber)).toEqual(12345)
  })
  test('should return default message for status code', () => {
    let tempMockMessageObjStatusCode = { ...mockMessageObj
    }
    tempMockMessageObjStatusCode["statusCode"] = ""
    expect(helper.returnLogMessage(tempMockMessageObjStatusCode)).toEqual('Status code not defined || Testing log || userId : tne:1***:***2354')
  })
  test('should return default message for log msg', () => {
    let tempMockMessageObjLogMsg = { ...mockMessageObj
    }
    tempMockMessageObjLogMsg["logMsg"] = ""
    expect(helper.returnLogMessage(tempMockMessageObjLogMsg)).toEqual('200 || Log message not defined || userId : tne:1***:***2354')
  })
  test('should return default message for masked data for undefined', () => {
    let tempMockMessageObjMaskData = { ...mockMessageObj
    }
    tempMockMessageObjMaskData["maskedData"] = ""
    expect(helper.returnLogMessage(tempMockMessageObjMaskData)).toEqual('200 || Testing log || Masked data not defined')
  })
  test('should return default message for masked data for empty object', () => {
    let tempMockMessageObjMaskData = { ...mockMessageObj
    }
    tempMockMessageObjMaskData["maskedData"] = {}
    expect(helper.returnLogMessage(tempMockMessageObjMaskData)).toEqual('200 || Testing log || Masked data not defined')
  })
  // getFileName Method Test Cases
  test('should return default label when module is not passed', () => {
    expect(helper.getFileName({})).toEqual('Module not passed while requiring the logger')
  })
})

// this one how can i test the custom format method
describe('custom format', () => {
  test('should call the printF function inside customFormat function', () => {
    
  })
})


// __mocks/winston.js
const winston = jest.genMockFromModule('winston');


const mockLoggerObject = {
  error: jest.fn(),
  info: jest.fn(),
};

const mockLabel = jest.fn();
const mocktimestamp = jest.fn();
const mockPrintf = jest.fn();
const mockCombine = jest.fn();
const mockDailyTransport = jest.fn();
const mockTransports = {
  DailyRotateFile: mockDailyTransport
};
const mockCreateLogger = jest.fn().mockReturnValue(mockLoggerObject);
const mockFormat = {
  label: mockLabel,
  timestamp: mocktimestamp,
  printf: mockPrintf,
  combine: mockCombine,
};


winston.createLogger = mockCreateLogger;
winston.transports = mockTransports;
winston.mockLoggerObject = mockLoggerObject;
winston.format = mockFormat;
winston.mockLabel = mockLabel;
winston.mocktimestamp = mocktimestamp;
winston.mockPrintf = mockPrintf;
winston.mockDailyTransport = mockDailyTransport;
winston.mockCombine = mockCombine;


module.exports = winston;


//__mocks/express-http-context

const httpContext = jest.genMockFromModule('express-http-context');

const mockGet = jest.fn();

httpContext.get = mockGet;

module.exports = httpContext;

我无法检查的两个测试用例或抛出错误的一个是

// helper.test.js

describe('custom format', () => {
    test('should call the printF function inside customFormat function', () => {
        // how can i coverage the line what should be written here
    })
})

// logger.test.js

test('expect Dailytransports to be called', () => {
    expect(winston.mockDailyTransport).toHaveBeenCalled(); 
  });

0 个答案:

没有答案