I'm going to leave this as an answer for how I've been handling this lately. So next time I'm Googling and end up here I'll remember π
β jest.mock
Without Success
I've tried using jest.mock
like I normally would on imports for the Logger
class from @nest/common
, but it seems to cause its own problems. Even if you try to keep the original implementation like so:
jest.mock('@nestjs/common', () => ({
...jest.requireActual('@nestjs/common'),
Logger: jest.fn(),
}))
I still want to believe there has to be a way to accomplish it like this, but maybe Nest JS's dependency system circumvents Jest's hoisted mocking? π€·
β
Using a Custom Logger with Nest JS's Dependency Injection
This feels like unnecessary lifting, but it follows Nest JS's dependency injection, and allows for extending or overwriting later. If you're already writing tests for Nest JS you're likely familiar with it already.
custom.logger.ts
import { ConsoleLogger } from '@nestjs/common'
export class CustomLogger extends ConsoleLogger {}
some-consumer.spec.ts
This approach uses the jest-mock-extended
library, but you could also do something like @Jay McDoniel
's answer as well.
import { SomeConsumer } from './some-consumer'
import { CustomLogger } from './custom.logger'
import { Test } from '@nestjs/testing'
import { mockDeep } from 'jest-mock-extended'
describe('SomeConsumer', () => {
let someConsumer: SomeConsumer
const logger = mockDeep<CustomLogger>()
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [
SomeConsumer,
{
provide: CustomLogger,
useValue: logger,
},
],
}).compile()
someConsumer = module.get(SomeConsumer)
})
it('should do something', () => {
const result = someConsumer.doSomething()
expect(result).toEqual('something returned')
})
it('should log something', () => {
someConsumer.doSomething()
expect(logger.log).toHaveBeenCalledWith('something')
})
})
some-consumer.ts
I figured I would provide an example of the logger being consumed.
import { Injectable } from '@nestjs/common'
import { CustomLogger } from './custom-logger'
@Injectable()
export class SomeConsumer {
constructor(private readonly logger: CustomLogger) {}
public doSomething(): string {
this.logger.log('something')
return 'something returned'
}
}
β
A Second Try with the imported Logger
from @nestjs/common
I saw elsewhere someone mentioning you could set the logger in the module, so I gave it a shot and it seems to work as well π
some-consumer-imported.ts
import { Injectable, Logger } from '@nestjs/common'
@Injectable()
export class SomeConsumerImported {
private logger = new Logger(SomeConsumerImported.name)
public doSomething(): string {
this.logger.log('something logged')
return 'something returned'
}
}
some-consumer-imported.spec.ts
import { SomeConsumerImported } from './some-consumer-imported'
import { Logger } from '@nestjs/common'
import { Test } from '@nestjs/testing'
import { mockDeep } from 'jest-mock-extended'
describe('SomeConsumerImported', () => {
let someConsumerImported: SomeConsumerImported
const logger = mockDeep<Logger>()
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [SomeConsumerImported],
}).compile()
module.useLogger(logger)
someConsumerImported = module.get(SomeConsumerImported)
})
it('should do something', () => {
const result = someConsumerImported.doSomething()
expect(result).toEqual('something returned')
})
it('should log something', () => {
someConsumerImported.doSomething()
expect(logger.log).toHaveBeenCalledWith('something logged', SomeConsumerImported.name)
})
})