【问题标题】:Error while unit testing HttpClientModule (Angular 4.3+)单元测试 HttpClientModule 时出错(Angular 4.3+)
【发布时间】:2017-07-26 12:25:25
【问题描述】:

我正在探索新的 Angular HttpClientModule 并遇到莫名其妙的错误。该模块足够新,以至于我还找不到有关如何进行单元测试的任何有用信息,官方文档也没有任何示例。

该应用程序包含一个具有一种方法的服务,该方法将 URL 传递给 http.get。当我在浏览器上下文(又名ng serve)中调用此方法时,http 调用会正常执行。在单元测试的上下文中调用时,我收到以下错误:

TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.

这是使用 Angular CLI 生成的最小应用程序。以下是相关代码:

app.module.ts:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, HttpClientModule],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.ts:

import { Component, OnInit } from '@angular/core';
import { TestService } from './test.service'

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  providers: [TestService]
})
export class AppComponent {
  title = 'app';

  constructor(private testSvc: TestService) {}

  ngOnInit() {
    this.testSvc.executeGet('http://www.example.com');
  }
}

test.service.ts:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable()
export class TestService {
  constructor(private http:HttpClient) {}

  executeGet(url:string) {
    this.http.get(url)
      .subscribe(
        data => console.log('success', data),
        error => console.log('error', error)
    );
  }
}

test.service.spec.ts:

import { HttpClient, HttpHandler } from '@angular/common/http';
import { TestBed, inject } from '@angular/core/testing';
import { TestService } from './test.service';

describe('TestService', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [HttpClient, HttpHandler, TestService]
    });
  });

  it('should executeGet', inject([TestService], (svc: TestService) => {
    expect(svc.executeGet).toBeDefined();
    // this is where the 'undefined' error occurs
    svc.executeGet('http://www.example.com'); 
  }));
});

非常感谢任何指导或指示。

【问题讨论】:

  • 你真的需要一个真正的 HttpClientModule 吗?根据documentation - HTTP 后端需要作为良好测试实践的一部分进行模拟。
  • @AleksandrPetrovskij 我已经阅读了可用的文档。它很薄,而且 HttpClientModule 还很年轻,没有太多关于如何使用它的第三方内容。我在尝试使用 HttpClientTestingModule 时遇到了其他问题,但选择不将它们包含在这篇文章中,以使其专注于“未定义”错误。无论如何,

标签: angular


【解决方案1】:

最近我遇到了与错误消息完全相同的问题:

TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.

对我来说,这个错误的根源是没有正确使用 HttpInterceptor。如果您还提供自定义 HttpInterceptor,请确保正确使用它。在下面的代码 sn-p 中,请注意如果错误状态不是 401,我是如何错过返回 Observable, Promise, Array, or Iterable 的。默认情况下,undefinedintercept 方法返回,而不是 Observable, Promise, Array, or Iterable,所以 angular 会抱怨这一点。

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(req).catch(err => {
      if(err instanceof HttpErrorResponse) {
        if(err.status === 401) {
          this.store.dispatch(this.authAction.unauthorized(err));
          return Observable.throw(err);
        }
      }
    })
  }

解决方法是遵循代码 sn-p。

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(req).catch(err => {
      if(err instanceof HttpErrorResponse) {
        if(err.status === 401) {
          this.store.dispatch(this.authAction.unauthorized(err));
        }
      }
      return Observable.throw(err); //Actually this line of code
    })
  }

【讨论】:

  • 我没有使用 HttpInterceptor。正如我之前提到的,TestService.executeGet 在浏览器上下文中运行时有效,但在单元测试中失败。环境差异似乎是导致问题的原因。
  • 如何对拦截器进行单元测试。你有样品要分享吗?
【解决方案2】:

缺少 HttpClientModule 的导入

Here is the working example on plnkr

describe('TestService', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientModule],
      providers: [TestService]
    });
  });

  it('should executeGet', () => {
    const testService = TestBed.get(TestService);

    expect(testService.executeGet).toBeDefined();
    testService.executeGet('http://www.example.com');
  }));
});

【讨论】:

  • 谢谢你的例子。我很感激。也就是说,它并不能帮助我理解错误,也不能帮助我理解为什么没有模拟就不可能进行单元测试。我理解为什么在测试中模拟是一个好主意,但不明白为什么在没有模拟的情况下 HTTP 调用会失败,这是我问题的症结所在。
  • 我认为 HttpClientModule 的导入丢失,example
  • 就是这样!再次感谢,亚历山大。如果您想更新答案或发布其他答案,我很乐意接受。
猜你喜欢
  • 2012-06-12
  • 1970-01-01
  • 2018-05-03
  • 2020-10-12
  • 1970-01-01
  • 2021-05-18
  • 1970-01-01
  • 2014-09-13
  • 1970-01-01
相关资源
最近更新 更多