Reputation: 1
As part of an Angular 4 project, I am desperately trying to test with Jasmine a function which implement an RxJs chain/sequence with operators (map
in my case).
class RendezVousResolver {
searchRendezVous(code: string): Observable<any> {
return Observable.create(observer => {
this.userCardService.readCard(code).map(userData => {
this.rendezVousService.search(userData).subscribe(
result => {
observer.next(result);
},
error => {
observer.error(error);
}
);
});
});
}
}
My unit test uses 2 mocks in order to "simulate" the 2 services' layers: userCardService
and rendezVousService
.
class MockUserCardService {
readCard(code: string): Observable<any> {
return Observable.of('<data></data>');
}
}
class MockRendezVousService {
search(userData : string): Observable<any> {
return Observable.of({
rdvs: []
});
}
}
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
RendezVousResolver,
{ provide: RendezVousService, useClass: MockRendezVousService },
{ provide: SwcVitaleReadingService, useClass: MockSwcVitaleReadingService }
]
});
fixture = TestBed.get(RendezVousResolver);
});
And here my unit test.
it('should return the expected response', async(() => {
fixture.resolve(undefined, undefined).subscribe(
rdvs => {
console.log("expect");
expect(rdvs).toEqual({
rdvs: []
});
},
error => {
console.log("fail");
fail('No error was expected');
}
);
}));
When I execute it, the test seems to not wait for the events emitted by the mocked Observable. Neither the expected
nor the fail
are executed. I'm sure about that because nothing is logged in the console.
The only way I found to make this test passed is to not use the operator map
and replace my code with nested subscriptions.
searchRendezVous(code: string): Observable<any> {
return Observable.create(observer => {
this.userCardService.readCard(code).subscribe(
userData => {
this.rendezVousService.search(userData).subscribe(
rdvs => {
observer.next(rdvs);
},
error => {
observer.error(error);
}
)
},
error => {
observer.error(error);
}
);
});
}
I encountered the same problem with others operators than map
(zip
for example).
Thank you for your help.
Upvotes: 0
Views: 5445
Reputation: 9425
You can simplify your RendezVousResolver
by swapping Observable.create
for existing behaviour / functions in RxJs:
class RendezVousResolver {
searchRendezVous(code: string): Observable<any> {
return this.userCardService.readCard(code)
.mergeMap(userData => this.rendezVousService.search(userData));
}
}
That way you have less edge cases to catch yourself.
Testing this can be done without time by swapping the readCard
and search
with mocks returning a Rx.Observable.from([])
with the expected mock data. Simply invoking .toPromise()
on your searchRendezVous()
will make that work without any scheduler magic.
it('returns data', () => {
return searchRendezVous('foo')
.toPromise()
.then(searchResults => {
expect(searchResults).to.not.be.empty();//assert what you need
})
});
Upvotes: 3