我有一个控制器,它通过在控制器的构造函数中注入依赖项来使用NestJS内置Logger:
constructor(private readonly logger: Logger)
我希望能够在我的Jest测试中模拟它,看看在日志记录过程中调用了哪些方法和哪些参数。我尝试了以下语法:
providers[{
provide: Logger,
useValue: {
log: jest.fn(),
}
}]
在这种情况下,这一行:
expect(Logger).toHaveBeenCalledTimes(1);
退货:匹配器错误:接收到的值必须是模拟或间谍函数
如有任何帮助,我们将不胜感激!
在测试中,您应该使用moduleFixture.get(Logger)
(或类似的东西)将记录器从DI上下文中取出,然后检查expect(logger.log).toHaveBeenCalledTimes(1)
。Logger
本身就是一个类,而不是间谍或模拟,所以Jest不知道该怎么处理它。
有效的完整解决方案:
import { Test } from '@nestjs/testing';
let logger: Logger;
beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
providers: [
{
provide: Logger,
useValue: {
log: jest.fn(),
},
},
],
}).compile();
logger = moduleRef.get<Logger>(Logger);
});
然后在稍后的测试中:
expect(logger.log).toHaveBeenCalledTimes(1);
expect(logger.log).toHaveBeenCalledWith('Your log message here')
我将把这作为我最近如何处理这件事的答案。所以下次我在谷歌上搜索并在这里结束时,我会记得
❌jest.mock
不成功
我已经尝试过像通常从@nest/common
导入Logger
类时那样使用jest.mock
,但它似乎会导致自身的问题。即使你试图保持原来的实现方式:
jest.mock('@nestjs/common', () => ({
...jest.requireActual('@nestjs/common'),
Logger: jest.fn(),
}))
我仍然想相信一定有办法做到这一点,但也许Nest JS的依赖系统绕过了Jest的嘲笑?
✅使用带有Nest JS依赖项注入的自定义记录器
这感觉像是不必要的提升,但它遵循了NestJS的依赖注入,并允许稍后进行扩展或覆盖。如果你已经在为NestJS编写测试了,你可能已经很熟悉了。
custom.logger.ts
import { ConsoleLogger } from '@nestjs/common'
export class CustomLogger extends ConsoleLogger {}
some-consumer.spec.ts
这种方法使用jest-mock-extended
库,但您也可以执行类似@Jay McDoniel
的答案的操作。
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
我想我会提供一个记录器被消耗的例子。
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'
}
}
✅从@nestjs/common
导入Logger
的二次尝试
我在其他地方看到有人提到你可以在模块中设置记录器,所以我尝试了一下,它似乎也能正常工作
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)
})
})
您可以直接在Logger
类上使用jest.SpyOn
jest.spyOn(Logger, 'log');
expect(Logger.Log).toHaveBeenCalledTimes('error', 'SmsService.sendSMS');