【问题标题】:Nest JS two instances of the same provider嵌套 JS 同一个提供者的两个实例
【发布时间】:2019-08-06 02:24:04
【问题描述】:

您好,在测试套件中,我认为我有 2 个同一个提供者的活动实例,一个用于实现,另一个用于实际实现。

我的结论基于一个事实,即在我的测试中,我尝试用 jest.fn 调用替换方法,但在我正在测试的服务上,该方法仍然指向原始实现。

更奇怪的是,我能够模拟另一个执行完全相同的过程的服务,就好像取决于这些服务的注入方式(它们在容器图中的来源)它会起作用或不起作用。

我会尝试分享一些sn-ps,但当然,只有一个小的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% 肯定在被测服务上调用了这两种方法,但由于某种原因,第二个方法没有被 mock 取代

【问题讨论】:

    标签: javascript node.js typescript jestjs nestjs


    【解决方案1】:

    您正在混合单元测试和端到端 (e2e) 测试的概念。您正在导入一个模块,同时您直接导入单个提供程序。我假设您导入的模块之一也导入了ExamCacheResultService。这样,您的测试应用程序中就有两个。当您调用app.get(ExamCacheResultService) 时,您将获得直接在测试模块中声明的实例。但是当您调用finishApplication 时使用的是另一个。确定您要测试的内容并遵循以下原则:

    单元测试

    在单元测试中,您希望测试与其他依赖项隔离的单个提供程序/控制器,例如UserServiceUsersController。您将此提供程序及其注入的依赖项作为模拟导入。您不导入模块。

    假设我们有一个依赖于DatabaseConnectionUsersService

    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([]);
    

    E2E 测试

    在端到端测试中,您希望测试整个应用程序以及您事先进行单元测试的部分之间的交互。因此,您不要导入单个提供程序,而是导入一个模块,通常是 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();
    

    如何创建模拟?

    this answer

    【讨论】:

    • 虽然并不能真正解决问题,但这是一条非常有用的信息!
    • 我已经导入了提供程序,因为我遇到了一些不清楚的未解决依赖项的另一个问题。在稍微重组应用程序以删除一些“周期”后,我能够以与 e2e 非常相似的方式进行设置(即只有模块)。但是,对于某些依赖解决方案,有一些非常模糊的错误消息,最糟糕的是,仅用于测试。但是,在学习了一些调整之后,仍然会筑巢岩石
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-16
    • 1970-01-01
    • 2021-07-10
    • 2011-06-18
    • 2019-04-04
    • 2017-07-30
    • 1970-01-01
    相关资源
    最近更新 更多