Вот решение для модульного тестирования:
logger.ts
import { createLogger, format, transports } from 'winston';
import path from 'path';
console.log('process.env.LOG_LEVEL: ', process.env.LOG_LEVEL);
const loggerLevel = process.env.LOG_LEVEL || 'debug';
const logDir = './log';
export function logger(logFile) {
return new Promise((resolve) => {
resolve(
createLogger({
level: loggerLevel === 'undefined' ? 'info' : loggerLevel,
format: format.combine(
format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss',
}),
format.printf((info) => `${info.timestamp} ${info.level}: ${info.message}`),
),
transports: [new transports.File({ filename: path.join(logDir, logFile) })],
}),
);
});
}
logger.test.ts
:
import { Logger } from 'winston';
import { Format, TransformableInfo } from 'logform';
jest.mock('winston', () => {
const mFormat = {
combine: jest.fn().mockReturnThis(),
timestamp: jest.fn().mockReturnThis(),
printf: jest.fn().mockReturnThis(),
};
const mTransports = {
File: jest.fn(),
};
return { createLogger: jest.fn(), format: mFormat, transports: mTransports };
});
describe('59857333', () => {
afterEach(() => {
jest.resetModules();
});
it.each`
LOG_LEVEL | expected
${'debug'} | ${'debug'}
${'undefined'} | ${'info'}
${''} | ${'debug'}
`('should pass with LOG_LEVEL: $LOG_LEVEL', async ({ LOG_LEVEL, expected }) => {
const ORIGINAL_LOG_LEVEL = process.env.LOG_LEVEL;
process.env.LOG_LEVEL = LOG_LEVEL;
const { logger: loggerProvider } = require('./logger');
const { createLogger, format, transports } = require('winston');
const logFile = 'access_log.txt';
let templateFunction!: (info: TransformableInfo) => string;
(format.printf as jest.MockedFunction<typeof format.printf>).mockImplementationOnce(function(this: Format, func) {
templateFunction = func;
return this;
});
const mLogger = {} as Logger;
(createLogger as jest.MockedFunction<typeof createLogger>).mockReturnValue(mLogger);
const logger = await loggerProvider(logFile);
expect(logger).toBeDefined();
expect(createLogger).toBeCalledWith({
level: expected,
format: expect.any(Object),
transports: [expect.any(Object)],
});
expect(format.combine).toBeCalledTimes(1);
expect(format.timestamp).toBeCalledWith({ format: 'YYYY-MM-DD HH:mm:ss' });
expect(format.printf).toBeCalledWith(templateFunction);
expect(transports.File).toBeCalledWith({ filename: 'log/access_log.txt' });
// test templateFunction
const info = { timestamp: 666, level: expected, message: 'connection success' };
const logEntry = templateFunction(info);
expect(logEntry).toBe(`666 ${expected}: connection success`);
process.env.LOG_LEVEL = ORIGINAL_LOG_LEVEL;
});
});
Результаты модульного теста со 100% покрытием:
PASS src/stackoverflow/59857333/logger.test.ts (12.615s)
59857333
✓ should pass with LOG_LEVEL: debug (817ms)
✓ should pass with LOG_LEVEL: undefined (9ms)
✓ should pass with LOG_LEVEL: (26ms)
console.log src/stackoverflow/59857333/logger.ts:486
process.env.LOG_LEVEL: debug
console.log src/stackoverflow/59857333/logger.ts:486
process.env.LOG_LEVEL: undefined
console.log src/stackoverflow/59857333/logger.ts:486
process.env.LOG_LEVEL:
-----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
-----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
logger.ts | 100 | 100 | 100 | 100 | |
-----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 14.8s
Исходный код: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59857333