George Edwards
George Edwards

Reputation: 9229

Unit Testing complex Observable flows

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

Upvotes: 3

Views: 4429

Answers (1)

JusMalcolm
JusMalcolm

Reputation: 1431

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);

Upvotes: 2

Related Questions