Reputation: 688
I'm exploring the new Angular HttpClientModule and running into an inexplicable error. The module is new enough that I can't yet find any useful information about how to unit test, and the official documentation doesn't have any examples.
The app contains a service with one method, which passes a URL to http.get
. When I call this method in a browser context (aka ng serve
), the http call executes normally. 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 app generated with Angular CLI. Here's the pertinent 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 greatly appreciated.
Upvotes: 3
Views: 1559
Reputation: 1243
Import of HttpClientModule missing
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');
}));
});
Upvotes: 1
Reputation: 4625
Recently I experienced exact same issue where the error message says:
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 properly. If you are also providing custom HttpInterceptor make sure you properly use it. In following code snippet notice how I missed returning Observable, Promise, Array, or Iterable
if error status is other than 401. By default undefined
is returned by the intercept
method instead of Observable, Promise, Array, or Iterable
so 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 to is was following code snippet.
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
})
}
Upvotes: 3