Unit testing error HttpClientModule (Angular 4.3+)
I am learning the new Angular HttpClientModule and am running an unexplained error. The module is new enough that I can't find any useful information on how to unit test yet, and there are no examples in the official documentation.
The app contains a service with one method that passes the url http.get
. When I call this method in the browser context (aka ng serve
) the HTTP call is ok. When called in the context of a unit test, I get the following error:
TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
This is a minimal application built with Angular CLI. Here's the relevant code:
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');
}));
});
Any guidance or pointers were greatly appreciated.
source to share
Importing HttpClientModule is missing
Here is a 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');
}));
});
source to share
I recently experienced the same problem which says the error:
TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
For me, the source of this error was not using HttpInterceptor . If you provide a custom HttpInterceptor as well , make sure you use it correctly. In the following code snippet, notice how I missed returning Observable, Promise, Array, or Iterable
if the error status is different from 401. By default undefined
, the method returns intercept
instead Observable, Promise, Array, or Iterable
, which is why angular is complaining about that.
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);
}
}
})
}
and the fix is ββthe following piece of code.
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
})
}
source to share