Reputation: 7034
I have been trying to mock some service calls that return observables in my Angular app, but i simply can't create a valid observable that will trigger calls like catch()
or map()
in my code. For example:
My service:
create(): Observable<any> {
return this.http
.post('/api/stuff', { id: 123 })
.catch(this.handleError)
.map(this.extractData);
}
My spec:
let authHttpMock = mock(AuthHttp);
when(authHttpMock.post('/api/stuff', { id: 123 })).thenReturn(Observable.create(observer => {
observer.error(new Error('500!'));
}));
const myService = new MyService(instance(authHttpMock));
myService.create({ id: 123 }).subscribe(
result => {
expect(result).toBeTruthy();
}
);
The coverage analysis tells me that the handleError
method has never been executed. On the case of a successful observable, it also doesn't go through the extractData
method.
Where is that observable going to? How can i return a proper observable in order to test such calls?
Upvotes: 10
Views: 5854
Reputation: 19578
What if you subscribe to the error handler? E.g. in your spec:
const myService = new MyService(instance(authHttpMock));
myService.create({ id: 123 }).subscribe(
result => {
throw new Error('Not supposed to be here.')
},
err => expect(err).toBeDefined(),
);
(Although I don't think this should be the case).
This is how I mock the failed backend calls:
// first, I provide mock http in TestBed.configure...
...
providers:
{
provide: Http,
useFactory: (backend: ConnectionBackend, options: BaseRequestOptions) => new Http(backend, options),
deps: [MockBackend, BaseRequestOptions]
},
... // other providers, like your mockAuthHttp
]
// than in the test, I tell the mockBackend to fail.
// including other stuff here to, just to show a few more things that can be done
it('should handle errors if backend doesn\'t like us', async(() => {
let service = TestBed.get(MyService);
let mockBackend = TestBed.get(MockBackend);
mockBackend.connections.subscribe((c: any) => {
if (c.request.url.indexOf('/api/stuff') !== -1) {
// you can check stuff here, like c.request.method or c.request._body
const err: ResponseError = new ResponseError('Stuff not good.');
err.status = 404;
return c.mockError(err);
}
throw new Error('Wrong url called.');
});
myService.create('whatever').subscribe((r: any) => {
throw new Error('Should not have been a success.');
},
(err: any) => {
expect(err.message).toEqual('Stuff not good.');
});
}));
Upvotes: 0
Reputation: 2684
Somewhere in your test code I believe you need to have this code:
AuthHttp.post('/api/stuff', {id : 123 }).subscribe(data => {});
Upvotes: 2
Reputation: 2763
You need to create mockData first so that service can make use of it.There is 2 files below. Put your component and service name in place of COMPONENT_NAME & SERVICE_NAME respectively.
Spec File ts
describe('WalletComponent', () => {
let component: WalletManagementComponent;
let fixture: ComponentFixture<WalletManagementComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ReactiveFormsModule],
declarations: [COMPONENT_NAME],
schemas: [NO_ERRORS_SCHEMA],
providers: [
{provide: APP_BASE_HREF, useValue: '/'},
{provide: SERVICE_NAME, useClass: serviceMockData}
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(COMPONENT_NAME);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('Should create WalletComponent', () => {
expect(component).toBeTruthy();
});
});
You need to create directory called testHelper(or any name) in root directory
serviceMockData ts
import {Observable} from 'rxjs/Observable';
const result = {
data: [
{id: 0, name: 'Temp Data 1'},
{id: 1, name: 'Temp Data 2'},
]
};
export class serviceMockData {
constructor() {
}
getWalletAudits() {
return Observable.of(result).map(res => res);
}
}
Upvotes: 0
Reputation: 4848
There are two ways you can achieve this.
First would be, in your service method, use the following:
create(id:number):Obserable<any> {
let url:string = 'http://localhost:3000/api/stuff';
let data:any = {
id: id
};
return this.http.post(url, JSON.stringify(data))
.map((res:Response) => res.json());
.catch((error:any) => this.handleError(error))
}
The second method would be, call use the service as follows:
create(id:number):Obserable<any> {
let url:string = 'http://localhost:3000/api/stuff';
let data:any = {
id: id
};
return this.http.post(url, JSON.stringify(data))
.map((res:Response) => res.json());
}
And when you call the service method, use this:
this._service.create(123)
.subscribe((res:any) => {
//do whatever with success
},
(error:any) => {
this.handleError(error)
});
The method you are using to extract data, this.extractData()
, can be called instead of res.json()
inside the service, but overall it's the same result.
Hope this helps :)
Upvotes: -1