【问题标题】:How to test a service/component which depends on another service which in turn depends on Http service?如何测试依赖于另一个服务的服务/组件,而另一个服务又依赖于 Http 服务?
【发布时间】:2017-10-31 13:04:51
【问题描述】:

我对使用服务作为依赖项的组件进行单元测试有疑问,该服务器依赖于Http。 我正在阅读此文档:Test a component with an async service

我的代码与此示例中的代码完全相同:

  ngOnInit(): void {
    this.twainService.getQuote().then(quote => this.quote = quote);
  }

顺便说一下,这是我的代码:code

文档说,当我测试依赖于其他服务的组件时,我必须:

  1. 将此服务设置为模块提供者
  2. 获取服务注入组件:twainService = fixture.debugElement.injector.get(TwainService);

  3. 设置间谍:spy = spyOn(twainService, 'getQuote').and.returnValue(Promise.resolve(testQuote));

我也这样做: 这是我的规格文件:spec file。 我在第 21 行提供服务,在第 29 行获得注入服务,在第 32 行设置间谍。

所以问题是:如果我做的一切都像文档说我收到错误:Error: No provider for Http!。显然出现这个错误是因为我的GoodsDataService 依赖于Http 服务。我应该如何处理?我这样做了:我创建了简单的 javascript 对象,并用它模拟了我的真实GoodsDataService。我还在这个对象中添加了getGoods 方法存根。所有这些让我可以在不注入真正的GoodsDataService 的情况下测试主要组件。但我完全不确定这个解决方案。我认为它很脏而且不正确。什么是单元测试组件/服务的正确方法,它依赖于另一个服务,而另一个服务又依赖于Http 服务?有什么想法吗?

【问题讨论】:

    标签: angular unit-testing http


    【解决方案1】:

    只需在测试模块的导入中添加HttpModule

    它必须存在,这样才能将 Http 注入您的服务中,但由于您监视服务方法,因此不会在您的测试中使用 Http。

    【讨论】:

    • 我认为这真是个好主意。我也在想同样的事情。但是为什么文档不说呢?有什么明显的吗?
    • 我不会说这很糟糕,因为了解一切如何运作需要一些时间。但我想说,一旦你了解它是如何工作的,这听起来很合乎逻辑。我同意,鉴于他们将 twain 服务作为使用 HTTP 的服务引入,他们应该解释如何在测试中提供假 Http 服务,或者如何导入 HttpModule。你应该打开一个问题。
    【解决方案2】:

    总结:是的,结合使用依赖存根和异步服务 Angular 文档示例。

    原来TwainService 实际上根本没有使用Http 或其他依赖项,它只是返回一个Promise:

    getQuote(): Promise<string> {
        return new Promise(resolve => {
          setTimeout( () => resolve(this.nextQuote()), 500 );
        });
    }
    

    因此,因为在我们的案例中,被测组件依赖于可能具有大量自身依赖项的服务,所以最好通过 Angular 文档中的 WelcomeComponent/userServiceStub 示例模拟该服务(继续阅读警告)。存根对象将有一个真实方法名称的属性,它的值是一个未命名的函数及其语句。

    现在,很容易在存根中错误地实现一个同步返回的方法,即使真正的方法是异步返回的。您不想这样做,因为被测组件可能包含一个 .then() 块来处理真正的异步方法的响应。因此,请确保您的 stub 方法返回包装在 promise 中的 testing-return-value:return Promise.resolve(mySwappableTestingReturnValue)

    最后,因为我们确实在测试异步方法(警告),it 块也应该使用TwainService 示例中的三个选项之一:angular 的async()/whenStable.then(),angular 的@987654332 @/tick(),或茉莉的done。我还发现附加到fixture.componentInstance.myAsyncMethod() 的promise 的常规.then() 块同样有效(也可能是js 的async/await)。

    ~~~

    作为一名间谍新手,我还犯了另一个错误,.and.callThrough() 可以帮助你。

    例如(幽默我,它很短而且很常见):LoginComponent 有一个 submit-btn,其 onclick 由调用 this.authService.askAuthServer 的 logMeIn 方法处理。 askAuthServer 使用 Http。

    测试按钮点击是否正确调用 logMeIn 的规范是同步的,我们不关心 logMeIn 的返回值,我们只关心它是否被调用(并且 UI 事件在主线程上同步处理,如果对你有帮助)。在 logMeIn 中测试决策逻辑的规范确实需要返回值,因此需要异步测试,所以假设我们使用 whenStable().then()

    所以,你很高兴在beforeEach 中创建了一个 spyOn logMeIn,但点击规范成功,异步逻辑规范失败。在点击规范中,您会注意到 askAuthServer 没有执行,尽管我们的间谍成功.toHaveBeenCalled()。到底是怎么回事?这是因为间谍阻止了 logMeIn 的执行,甚至在我们选择不等待 logMeIn 的返回之前。所以在逻辑规范中,虽然它可能不使用 logMeInSpy,但即使我们正确设置了异步部分,间谍仍然在那里阻止 logMeIn(以及 askAuthServer)的执行。

    了解这一点,要么在 beforeEach 中创建间谍时使用 .and.callThrough(),以允许在两个规范期间执行 logMeIn,或者将间谍移动到它不会的点击规范中影响逻辑规范。

    【讨论】:

      猜你喜欢
      • 2014-09-04
      • 1970-01-01
      • 1970-01-01
      • 2019-03-18
      • 2020-07-09
      • 1970-01-01
      • 2019-10-19
      • 2020-01-27
      • 1970-01-01
      相关资源
      最近更新 更多