我正按照官方Nestjs documentation所述,使用AuthModule
测试我的supertest
控制器 HTTP层。该模块使用从EmailService
导入的EmailModule
。
我知道您可以按以下方式覆盖提供程序:
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [
AuthModule,
],
})
.overrideProvider(EmailService)
.useValue(buildMock(EmailService))
但是这不起作用,我假设是因为EmailModule
被导入到AuthModule
中。一种解决方法是.overrideProvider(...).useValue(...)
中的EmailModule
中的每个提供程序,但是这种无意义的做法是,我不得不模拟EmailModule
导入的模块。
当我进行AuthModule
的e2e测试时,我真的不关心EmailModule
的工作方式。我需要模拟的是EmailService
,并确保我的身份验证模块与该服务正确交互。
不幸的是,Test.createTestingModule({})
没有.overrideModule()
方法。
我尝试开玩笑地嘲笑EmailModule
:
jest.mock('../../src/email/email.module.ts', () => {
@Module({})
class EmailModule {
}
return EmailModule;
});
但我收到此错误:
Error: Nest cannot create the module instance. Often, this is because of a circular dependency between modules. Use forwardRef() to avoid it.
(Read more: https://docs.nestjs.com/fundamentals/circular-dependency)
Scope [RootTestModule -> AppModule]
有人知道如何实现吗?
答案 0 :(得分:2)
首先,我认为您对自己编写的测试类型有错误的印象。
让我们把事情弄清楚:
因此,很可能,如果您正在使用 supertest 编写测试,则很可能您正在编写 E2E 测试,并且通常情况下,您不想在 E2E\API 测试中模拟事物强>.
由于我们正在编写单元测试,因此我们可以在不使用原始 EmailModule 的情况下模拟您的依赖项。这是因为我们改写了 Test.createTestingModule
。我们可以直接从 EmailModule 导入使用的服务。
假设我有以下模块:
@Module({
imports: [ UserModule, RepoModule, PullRequestModule ],
controllers: [WebhookEventManagerController],
providers: [
WebhookEventManagerService
]
})
export class WebhookEventManagerModule {}
假设我在 UserService
中使用了 WebhookEventManagerService
。因此,我创建了一个包含真实 WebhookEventManagerService
的测试模块,并为 nestjs 注入的服务提供模拟而不使用应用程序的真实模块:
const UserServiceMock = {
create: (...anything) => user,
findByName: noop
}
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
WebhookEventManagerService,
{
provide: UserService,
useValue: UserServiceMock
}
]
}).compile();
service = module
.get<WebhookEventManagerService>(WebhookEventManagerService);
});
it('test example', async () => {
jest.spyOn(UserServiceMock, 'findByName')
.mockImplementation(() => Promise.resolve());
await service.run();
expect(UserServiceMock.findByName).toHaveBeenCalledWith({});
expect(UserServiceMock.findByName).toHaveBeenCalledTimes(1);
});
noop
是从 lodash
导入的,但是您可以在 useValue
中随意实现模拟,因为在测试中运行 jest.spyOn
时您正在模拟特定方法.UserServiceMock
实现 jest.fn
并使其在全球范围内工作。这很可能是我能想到的最少设置的方法。.module.ts
文件编写单元测试。但是我在运行整个应用程序时会在 E2E\API 测试中测试它们。此外,在 E2E\API 测试中,无论我正在测试服务器的哪个部分,我都只是在创建完整的服务器端应用程序。它总是看起来像这样:
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule]
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
server = app.getHttpServer();
});
对于 API 测试(位于 <root>/test
中),不要模拟一般情况。