将 pino 记录器存根为 class 个实例

Stubbing pino logger as class instance

我有一个自定义日志服务,使用 pinoLogger 的子实例来记录一些事情。这是代码:

class LogService {
    ihmLogger = loggerPino.child({});

    async log(
        req: Request,
        level: number,
        message: string,
        additional?: string
    ): Promise<void> {
        //Context initialisation
        const logObj = this.initLogObj(
            req,
            ngxLoggerLevelToPinoLevel[level],
            message,
            additional
        );

        // Mapping log level with pino logger
        switch (ngxLoggerLevelToPinoLevel[level]) {
            case CONSTANTS.DEFAULT.LOG_NGX_LEVEL.ERROR:
                this.ihmLogger.error(logObj);
                break;
            case CONSTANTS.DEFAULT.LOG_NGX_LEVEL.WARM:
                this.ihmLogger.warn(logObj);
                break;
            case CONSTANTS.DEFAULT.LOG_NGX_LEVEL.INFO:
                this.ihmLogger.info(logObj);
                break;
            case CONSTANTS.DEFAULT.LOG_NGX_LEVEL.DEBUG:
                this.ihmLogger.debug(logObj);
                break;
        }
    }
    
    private initLogObj(
        req: Request,
        level: number,
        message: string,
        additional?: string
    ): ILog {
        const additionalObj = JSON.parse(additional || '{}');
        return {...};
    }
}

export default new LogService();

我正在尝试为此服务编写单元测试。我正在使用 node-request-http 成功模拟 Request 对象。 我的问题与此 child 元素有关。我只是想看看是否根据级别调用了正确的函数,但我无法正确访问它。以下是我尝试过的几种语法:

[...]
import logService from './log.service';
import { loggerPino } from '../utils/logger';
[...]


it("test 1", async () => {
    sinon.stub(loggerPino,'child').returns({
        error: sinon.spy()
    });
    await logService.log(request, 5, 'msg', JSON.stringify({}));

    console.log('A',logService.ihmLogger.error);
});
// >> A [Function: noop]

it("test 2", async () => {
    const logger = sinon.stub(logService, 'ihmLogger').value({
        error: sinon.spy()
    });
    await logService.log(request, 5, 'msg', JSON.stringify({}));

    console.log('B',logger.error);
});
// >> B undefined

it("test 3", async () => {
    const logger = sinon.stub(logService, 'ihmLogger').value({
        error: sinon.spy()
    });
    await logService.log(request, 5, 'msg', JSON.stringify({}));

    console.log('C',logService.ihmLogger.error);
});
// >> C [Function (anonymous)]

我永远无法访问“callCount”值,更不用说用 getCall(0).args[0]

检查给定的参数了

我应该如何编写存根才能正确访问我要测试的值?

这里的解决方案是完全替换服务的 ihmLogger 属性,就像上面的“B”尝试所做的那样,但是把它放在一个变量中并检查变量,而不是 sinon.stub 对象返回:

it("Log error called", async () => {
    const customLogger = {
        trace: sinon.spy(),
        debug: sinon.spy(),
        info: sinon.spy(),
        warn: sinon.spy(),
        error: sinon.spy(),
        fatal: sinon.spy()
    };
    sinon.stub(logService, 'ihmLogger').value(customLogger);
    await logService.log(request, 5, 'msg', JSON.stringify({}));

    expect(customLogger.error.callCount).to.eq(1);
    expect(customLogger.fatal.callCount).to.eq(0); //double check to avoid false positive
});

我不是 unit-testing pinoLogger 对象(我必须相信是否按预期工作),我是 unit-testing 正确的分支和调用。 这样做,我还可以检查传输的 logObj :

const builtObj = customLogger.error.getCall(0).args[0]