使用Jest模拟fs函数:新手如何测试Winston Logger类
Hey there! Since you're new to ES6 and Jest, let's walk through how to test your Winston Logger class properly. First, I'll assume a complete version of your Logger class (since your code snippet got cut off), then we'll build out the tests step by step.
First, Let's Finalize Your Logger Class
Here's a plausible complete version based on your snippet (fill in any gaps to match your actual code):
const winston = require('winston'); const fs = require('fs'); const path = require('path'); const config = require('../config.json'); class Logger { static logDir = path.join(__dirname, '../logs'); constructor() { Logger.createLogDir(Logger.logDir); this.logger = winston.createLogger({ level: config.logLevel || 'info', format: winston.format.json(), transports: [ new winston.transports.Console({ format: winston.format.combine( winston.format.colorize(), winston.format.simple() ) }), new winston.transports.File({ filename: path.join(Logger.logDir, 'error.log'), level: 'error' }), new winston.transports.File({ filename: path.join(Logger.logDir, 'combined.log') }) ] }); } static createLogDir(dir) { if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } } info(message) { this.logger.info(message); } error(message) { this.logger.error(message); } } module.exports = Logger;
The key here is to mock external dependencies (like fs and winston) so we don't create real files or directories during tests—this keeps tests fast and isolated.
1. Set Up Mocks
Create a test file (e.g., logger.test.js) and start by mocking the modules we don't want to run for real:
const Logger = require('./Logger'); const winston = require('winston'); const fs = require('fs'); // Mock the fs module to avoid real directory creation jest.mock('fs'); // Mock winston to track calls to its logger methods jest.mock('winston', () => { const mockLogger = { info: jest.fn(), error: jest.fn() }; return { createLogger: jest.fn(() => mockLogger), transports: { Console: jest.fn(), File: jest.fn() }, format: { json: jest.fn(), combine: jest.fn(), colorize: jest.fn(), simple: jest.fn() } }; });
2. Test Directory Creation Logic
We want to verify that createLogDir works as expected—creating the directory only when it doesn't exist:
describe('Logger', () => { // Reset all mock calls between tests to avoid cross-test contamination beforeEach(() => { jest.clearAllMocks(); }); test('creates log directory when it does not exist', () => { // Mock fs.existsSync to pretend the directory doesn't exist fs.existsSync.mockReturnValue(false); new Logger(); expect(fs.existsSync).toHaveBeenCalledWith(Logger.logDir); expect(fs.mkdirSync).toHaveBeenCalledWith(Logger.logDir, { recursive: true }); }); test('does NOT create log directory when it already exists', () => { // Mock fs.existsSync to pretend the directory exists fs.existsSync.mockReturnValue(true); new Logger(); expect(fs.existsSync).toHaveBeenCalledWith(Logger.logDir); expect(fs.mkdirSync).not.toHaveBeenCalled(); }); });
3. Test Winston Logger Initialization
Verify that the constructor sets up the Winston logger with the correct configuration:
test('initializes Winston logger with correct config', () => { new Logger(); expect(winston.createLogger).toHaveBeenCalled(); // Check key config values (adjust to match your actual setup) const loggerConfig = winston.createLogger.mock.calls[0][0]; expect(loggerConfig.level).toBe('info'); // or config.logLevel if you're using that expect(loggerConfig.transports).toHaveLength(3); // Match the number of transports in your class });
4. Test Logging Methods
Make sure your info and error methods correctly delegate to the Winston logger:
test('calls Winston info method with the provided message', () => { const logger = new Logger(); const testMsg = 'User logged in successfully'; logger.info(testMsg); expect(winston.createLogger().info).toHaveBeenCalledWith(testMsg); }); test('calls Winston error method with the provided message', () => { const logger = new Logger(); const testMsg = 'Failed to connect to database'; logger.error(testMsg); expect(winston.createLogger().error).toHaveBeenCalledWith(testMsg); });
Quick Tips for Beginners
- Always use
jest.clearAllMocks()inbeforeEach—this ensures mocks don't retain calls from previous tests. - If your logger uses values from
config.json, mock that file too withjest.mock('../config.json', () => ({ logLevel: 'debug' }))to test different scenarios. - If you want to verify log formatting, you can spy on
console.log(for console transport) or mock file streams, but mocking is generally preferred for speed.
内容的提问来源于stack exchange,提问作者Oyabi

