javascript - 嵌套 JS 同一个提供者的两个实例
问题描述
嗨,在一个测试套件上,在我看来,我有 2 个同一个提供者的活实例,一个用于实现,另一个用于实际实现。
我的结论基于一个事实,即在我的测试中,我尝试用 jest.fn 调用替换方法,但在我正在测试的服务上,该方法仍然指向原始实现。
更奇怪的是,我能够模拟另一个执行完全相同过程的服务,就好像取决于这些服务的注入方式(它们在容器图中的来源)它会起作用或不起作用。
我会尝试分享一些片段,但当然,只有一个小的 repo 可以真正重现它,但也许有人有洞察力:
beforeAll(async done => {
app = await Test.createTestingModule({
imports: [
SOME_MODULES,
],
providers: [
EssayApplicationService,
ReviewFacade,
ExamCacheResultService,
],
}).compile();
essayApplicationService = app.get<EssayApplicationService>(EssayApplicationService)
reviewFacade = app.get<ReviewFacade>(ReviewFacade)
examCacheResult = app.get<ExamCacheResultService>(ExamCacheResultService)
await app.init()
done()
})
it('should invoke review only once', async done => {
reviewFacade.startReview = jest.fn() --> this works
examCacheResult.clearCachedResult = jest.fn() --> this fails
await essayApplicationService.finishApplication()
expect(reviewFacade.startReview).toHaveBeenCalledTimes(1)
expect(reviewFacade.startReview).toHaveBeenCalledWith(expect.objectContaining({ id: 1 }))
expect(examCacheResult.clearCachedResult).toHaveBeenCalledTimes(1) ---> here this fails, although it's called!!
所以,问题归结为这样一个事实,即我 100% 肯定这两种方法都在被测服务上调用,但由于某种原因,第二种方法没有被模拟所取代
解决方案
您正在混合单元测试和端到端(e2e)测试的概念。您正在导入一个模块,同时您直接导入单个提供程序。我假设您导入的模块之一也导入了ExamCacheResultService
. 这样,您的测试应用程序中就有两个。当您调用 时app.get(ExamCacheResultService)
,您将获得直接在测试模块中声明的实例。但是,当您调用时使用finishApplication
的是另一个。确定您要测试的内容并遵循以下原则:
单元测试
在单元测试中,您希望测试与其他依赖项隔离的单个提供程序/控制器,例如UserService
或UsersController
. 您将此提供程序及其注入的依赖项作为模拟导入。您不导入模块。
假设我们有一个UsersService
依赖于 a 的 a DatabaseConnection
:
export class UsersService {
constructor(private connection: DatabaseConnection) {}
// ...
}
在您的单元测试中,您导入UsersService
,模拟DatabaseConnection
但不导入UsersModule
.
module = await Test.createTestingModule({
providers: [
UsersService,
{ provide: DatabaseConnection, useClass: DbConnectionMock },
],
}).compile();
databaseMock = module.get(DatabaseConnection);
databaseMock.findMany.mockReturnValue([]);
端到端测试
在端到端测试中,您希望测试整个应用程序以及您事先进行单元测试的部分之间的交互。因此,您不要导入单个提供程序,而是导入一个模块,通常是AppModule
. 然后,您可以覆盖单个提供程序,例如,如果您想在内存数据库而不是实际数据库上进行测试,或者您想模拟外部 API 的结果。
const moduleFixture = await Test.createTestingModule({
imports: [AppModule],
}).overrideProvider(DatabaseConnection).useClass(InMemoryDatabaseConnection)
.overrideProvider(ExternalApiService).useValue(externalApiMock)
.compile();
app = moduleFixture.createNestApplication();
externalApiMock.get.mockReturnValueOnce({data: [...]});
await app.init();
如何创建模拟?
看到这个答案。
推荐阅读
- java - 如何通过 glassfish 服务器上的容器管理身份验证实现“首次登录时更改密码”
- python - 如何改进我的 python openCV 视频流?
- r - 为要在数据表的搜索栏中使用的特定值指定不同的名称
- sql - 获取在 2 列中具有相同值但在第 3 列中具有不同值的记录
- c# - .NET DateTime 在序列化时添加本地时间偏移量?
- java - 如何在房间持久性中返回数据的行数
- python - QuantLib-Python:使用 VanillaSwap 仪器的 quantlib Schedule 解决非正时间前向误差
- r - 计算 df2 的几列之间的平均值,该平均值可以根据 df1 的变量 `var1` 变化,并将该值添加到 df1 中的新变量
- pandas - 基于列中的值的示例 Pandas 数据框
- c# - 尝试从 VS 表单应用程序资源加载字体文件时出错