I have a custom log service, using a child instance of pinoLogger to log some things. Here is the code :
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();
I'm trying to write unit-tests for this service. I'm using node-request-http
to mock the Request object, successfully.
My problem concerns this child
element. I'm simply trying to see if the correct function is called depending on the level, but I can't access it correctly. Here are the few syntaxes I tried :
[...]
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)]
I never could access the "callCount" valut, let alone checking the given parameter with a getCall(0).args[0]
How should I write the stub to be able to correctly access the value i'm trying to test?
The solution here was to completely replace the ihmLogger property of the service, as done in the "B" try up there, but putting it in a variable and checking the variable, not the sinon.stub object returned :
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
});
I'm not unit-testing the pinoLogger object (I have to trust if to work as intended), I'm unit-testing the correct branch & call.
Doing this, I'm also able to inspect the logObj
transmitted :
const builtObj = customLogger.error.getCall(0).args[0]