Unit Testing complex Observable flows
angular, ngrx, observable, reactive-programming, rxjs
Solution
You are right, there is a timing issue with the test. By the time the first `Expect()` expression occurs, both mock `Responses` have returned. To fix your test, you can change one of the responses to use `setTimeout()` and set the `tick()` between expect clauses to some time after that delay.
eg:
if (req.method === RequestMethod.Get && req.url === 'api/getLine/data' && req.headers.get('range') === 'monthly') {
setTimeout(() => {
connection.mockRespond(new Response(new ResponseOptions({ body: { data: 15 } }))); }, 100);
}
and:
expect(value).toEqual(12);
tick(100);
expect(value).toEqual(15);
However, similarly to what julia said above, a better test would be to push the values into an array and check that the values at the respective indexes are correct. In this case I would recommend replacing `flatMap()` in your test function with `concatMap()`, which preserves order. You can then write multiple tests with each response delayed and still check that the values at the respective indexes are correct:
tick(100);
expect(values[0]).toEqual(12);
expect(values[1]).toEqual(15);
Problem
I have the following function, which (I think) returns a compound observable: ``` test(cell) { const observ1 = this.store.select('range').flatMap((val: IRange) => {return this.getCellOEE(val.value)}); const observ2 = this.getCellOEE(cell); return Observable.merge(observ1, observ2); } ``` I am using this in an angular 4 service, and trying to unit test with jasmine and karma. So I am mocking store like this: ``` class mockStore { select(): Observable<IRange> { return Observable.of({value: 'daily', start: moment(), end: moment()}, {value: 'monthly', start: moment(), end: moment()}) } } ``` and then I have the following test: ``` it('should update result on store change', fakeAsync(inject( [MockBackend, OeeService], (backend: MockBackend, s: OeeService) => { const urls = []; backend.connections.subscribe((connection: MockConnection) => { const req: any = connection.request; urls.push(req.url); if (req.method === RequestMethod.Get && req.url === 'api/getLine/data' req.headers.get('range') === 'daily') { connection.mockRespond(new Response(new ResponseOptions({ body: { data: 12 } }))); } if (req.method === RequestMethod.Get && req.url === 'api/getLine/data' && req.headers.get('range') === 'monthly') { connection.mockRespond(new Response(new ResponseOptions({ body: { data: 15 } }))); } }); let value; s.test('powders').subscribe(val => { value = val; }) tick(); expect(value).toEqual(12); tick(); expect(value).toEqual(15); }) )); ``` I hope it is clear what I am trying to achieve, so when the `test()` function is initially called, a straight forward http call is made, but then my mocked store should release a change in params, map that to an http request with the new params and then return a different value. I currently get the first `expect()` passing, but the second failing with: Expected 12 to contain 15 So I think there is a timing issue? Any ideas how I can resolve this? I think the test is important to verify, because then I can sue some good old TDD to prove whether this approach works. Update: Here is my full unit test file