将同一提供程序的两个实例嵌套JS [英] Nest JS two instances of the same provider

查看:129
本文介绍了将同一提供程序的两个实例嵌套JS的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在一个测试套件中,我似乎有2个同一个提供程序的活动实例,一个实例用于实现,另一个实例用于实际实现.

Hi on a test suite it appears to me that I have 2 living instances of a same provider, one for the implementation and another one for the real implementation.

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

I base my conclusion in a fact that on my test I tried replace a method by a jest.fn call but still, on the service I am testing the method still points to the original implementation.

更使我感到奇怪的是,我能够模拟另一个执行完全相同的过程的服务,好像取决于这些服务的注入方式(它们来自容器图中的位置)是否起作用.

What makes it even more odd is that I was able to mock another service performing exactly the same procedure, as if, depending on how those services were injected (where they came from in the container graph) it would or not work.

我将尝试分享一些片段,但是,当然,只有一个小仓库可以实际复制它,但是也许有人对此有见识:

I'll try to share some snippets, but of course, only a small repo could actually reproduce it, but perhaps someone has an insight:

 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%肯定的,但是由于某种原因第二种方法并未被模拟代替

So,the issue boils down to the fact that I'm 100% positive that both methods were called on the service under test, but the second for some reason wasn't replaced by the mock

推荐答案

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

You are mixing the concepts of unit tests and end to end (e2e) tests. You are importing a module and at the same time you import single providers directly. I'm assuming that one of your imported modules also imports ExamCacheResultService. This way, you have two of them in your test application. When you call app.get(ExamCacheResultService), you will get the instance that is directly declared in your testing module. But the one that is used when you call finishApplication is the other one. Decide what you want to test and follow the following principles:

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

In a unit test you want to test a single provider/controller isolated from other dependencies, e.g. UserService or UsersController. You import this provider and its injected dependencies as mocks. You do not import a module.

假设我们有一个UsersService依赖于DatabaseConnection:

Let's assume we have a UsersService that depends on a DatabaseConnection:

export class UsersService {
  constructor(private connection: DatabaseConnection) {}
  // ...
}

在单元测试中,导入UsersService,模拟DatabaseConnection,但是导入UsersModule.

In your unit test, you import the UsersService, you mock the DatabaseConnection but you do not import the UsersModule.

module = await Test.createTestingModule({
  providers: [
    UsersService,
    { provide: DatabaseConnection, useClass: DbConnectionMock },
  ],
}).compile();
databaseMock = module.get(DatabaseConnection);
databaseMock.findMany.mockReturnValue([]);

端到端测试

在端到端测试中,您要测试整个应用程序,并由此测试您已进行单元测试的部分之间的交互.因此,您不导入单个提供程序,而是导入一个模块,通常是AppModule.然后,您可以覆盖单个提供商,例如如果您要在内存数据库而不是实际数据库上进行测试,或者要模拟外部API的结果.

E2E test

In an end to end test, you want to test your whole application and therewith the interaction between the parts that you have unit tested beforehand. So you do not import single providers but instead a module, typically the AppModule. You can then override single providers, e.g. if you want to test on an in-memory database instead of an actual one or you want to mock the results of an external 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();

如何创建模拟?

请参见此答案.

这篇关于将同一提供程序的两个实例嵌套JS的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆