Angular 如何测试依赖于另一个服务(反过来又依赖于Http服务)的服务/组件?

Angular 如何测试依赖于另一个服务(反过来又依赖于Http服务)的服务/组件?,angular,unit-testing,http,Angular,Unit Testing,Http,我对将服务作为依赖项的组件进行单元测试有疑问,而此服务器依赖于Http。 我正在读这些文件: 我有与此示例中相同的代码: ngOnInit(): void { this.twainService.getQuote().then(quote => this.quote = quote); } 顺便说一下,这是我的代码: 文档中说,当我测试依赖于其他服务的组件时,我必须: 将此服务设置为模块提供程序 将服务注入组件:twainService=fixture.debugElem

我对将服务作为依赖项的组件进行单元测试有疑问,而此服务器依赖于
Http
。 我正在读这些文件:

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

  ngOnInit(): void {
    this.twainService.getQuote().then(quote => this.quote = quote);
  }
顺便说一下,这是我的代码:

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

  • 将此服务设置为模块提供程序
  • 将服务注入组件:
    twainService=fixture.debugElement.injector.get(twainService)

  • 安装spy:spy=spyOn(twainService,'getQuote')。和.returnValue(Promise.resolve(testQuote))

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


    所以问题是:如果我像docs那样做,我会得到错误:
    error:没有Http的提供者。显然,出现此错误是因为my
    GoodsDataService
    依赖于
    Http
    服务。我该怎么处理?我这样做了:我创建了简单的javascript对象,并用它模拟了我真正的
    GoodsDataService
    。我还在这个对象中添加了
    getGoods
    方法存根。所有这些都允许我在不注入real
    GoodsDataService
    的情况下测试主要组件。但我对这个解决方案一点也不确定。我认为这是肮脏的,不正确的。单元测试组件/服务的正确方法是什么,它依赖于另一个服务,而另一个服务又依赖于
    Http
    服务?有什么想法吗?

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


    它需要存在,以便可以将Http注入到您的服务中,但Http不会在您的测试中使用,因为您监视您的服务方法。

    摘要:是的,请结合使用依赖存根和异步服务示例

    事实证明,
    TwainService
    实际上根本没有使用
    Http
    或其他依赖项,它只是返回一个承诺:

    getQuote(): Promise<string> {
        return new Promise(resolve => {
          setTimeout( () => resolve(this.nextQuote()), 500 );
        });
    }
    
    getQuote():承诺{
    返回新承诺(解决=>{
    setTimeout(()=>resolve(this.nextQuote()),500);
    });
    }
    
    因此,因为在我们的例子中,被测试的组件依赖于一个服务,而该服务可能有许多自己的依赖项,所以最好通过Angular文档中的
    WelcomeComponent
    /
    userServiceStub
    示例来模拟该服务(请继续阅读以获取警告)。存根对象将有一个用于实际方法名的属性,其值是一个带有语句的未命名函数

    现在,很容易在存根中错误地实现同步返回的方法,即使实际方法异步返回。但是您不想这样做,因为被测试的组件可能包含一个
    .then()
    块来处理实际异步方法的响应。因此,请确保存根方法返回封装在承诺中的测试返回值:
    returnpromise.resolve(mySwappableTestingReturnValue)

    最后,因为我们确实在测试一个异步方法(注意事项),
    it
    块也应该使用
    TwainService
    示例中的三个选项之一:angular的
    async()
    /
    whenStable.then()
    、angular的
    fakeAsync()
    /
    tick()
    或jasmine的
    done
    。我还发现promise的常规
    .then()
    块附加到
    fixture.componentInstance.myAsyncMethod()
    上,同样有效(js的
    异步
    /
    也可能等待

    ~~~

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

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

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

    因此,您很高兴在每个
    之前的
    中创建一个spyOn logMeIn,但是click规范成功,而异步逻辑规范失败。在click spec中,您会注意到askAuthServer没有执行,尽管我们的spy成功地
    .tohaveBeenCall()
    。发生了什么事?那是因为间谍阻止了对洛格明的处决,甚至在我们选择不等待洛格明回来之前。因此,在逻辑规范中,尽管它可能不使用logMeInSpy,但间谍仍然在阻止logMeIn(以及askAuthServer)的执行,即使我们已经正确地设置了异步部分


    要理解这一点,在每个
    之前的
    中创建spy时,可以使用
    和.callThrough()
    ,以允许在两个规范中执行logMeIn,或者将spy移动到click规范中,这样做不会影响逻辑规范。

    我认为这确实是个好主意。我也在想同样的事情。但为什么医生不说呢?这是显而易见的吗?我不会说这是令人生畏的,因为了解每件事的运作方式需要一些时间。但我要说的是,一旦你了解了它是如何工作的,听起来就合乎逻辑了。我同意,考虑到他们将twain服务作为使用HTTP的服务引入,他们应该解释如何在测试中提供假的HTTP服务,或者如何导入HttpModule。你应该提出一个问题。