Testing Http Service making multiple calls and returning observable without mapping the responses
Asked Answered
G

1

8

I have a data service which fetches the data from server and makes multiple requests which then return an array of observables. I want to test the data.

What i tried doing is in the mockrespone I sent array which contains two observables i dont know if thats the right way to test data.

But tests are failing especially the last three tests in the async test block

Important: I want to test that, when setting charId to falsy and comicsId to falsy, calling tthe method, subscribing to the observable it returns, after you've mocked http, you get back an array containing the two expected responses. Same with the 4 expected response if charId is truthy. Same for the 6 expected responses when comicsId is truthy

// the service which fetches the data

getChar(): Observable<any> {

    const Observables = [];
    Observables.push(this.http.get('https://gateway.marvel.com:443/v1/public/characters?apikey'));
    Observables.push(this.http.get('https://gateway.marvel.com:443/v1/public/comics?apikey'));

    if (this.charId) {
      Observables.push(this.http.get(`${this.urlChar}${this.charId}${this.apiKey}`));
      Observables.push(this.http.get(`${this.urlChar}${this.charId}/comics${this.apiKey}`));
    }
    if (this.comicsId) {
      Observables.push(this.http.get(`${this.urlCom}${this.comicsId}${this.apiKey}`));
      Observables.push(this.http.get(`${this.urlCom}${this.comicsId}/creators${this.apiKey}`));
    }
    console.log([Observable, Observable]);
    return Observable.forkJoin(Observables);
  }
}

// my test

import { async, ComponentFixture, TestBed, getTestBed, inject } from '@angular/core/testing';
import { MockBackend, MockConnection } from '@angular/http/testing';
import { DataService } from './data.service';
import {
  BaseRequestOptions, Http, XHRBackend, HttpModule,
  Response, ResponseOptions, RequestMethod
} from '@angular/http';
import { Observable } from 'rxjs/Observable';

describe('DataService', () => {
  let mockBackend: MockBackend;

   beforeEach(async(() => {
    TestBed.configureTestingModule({
      providers: [
        DataService,
        MockBackend,
        BaseRequestOptions,
        {
          provide: Http,
          deps: [MockBackend, BaseRequestOptions],
          useFactory:
          (backend: XHRBackend, defaultOptions: BaseRequestOptions) => {
            return new Http(backend, defaultOptions);
          }
        }
      ],
      imports: [
        HttpModule
      ]
    });
    mockBackend = getTestBed().get(MockBackend);
  }));


  it('should get ObservableArr', (done) => {
    let dataService: DataService;

    getTestBed().compileComponents().then(() => {
      mockBackend.connections.subscribe(
        (connection: MockConnection) => {
          connection.mockRespond(new Response(
            new ResponseOptions({
              body: [Observable, Observable]
            }
            )));
        });

      dataService = getTestBed().get(DataService);
      expect(DataService).toBeDefined();

      dataService.getChar().subscribe((obsArr: Observable<any>[]) => {
        expect(obsArr.length).toBeDefined();
        expect(obsArr.length).toEqual(2);
        expect(obsArr.length).not.toBe(1);
        done();
      });
    });
  });


  it('should check the service',
    inject([DataService], (service: DataService) => {
      expect(service).toBeTruthy();
    }));

  it('should get ObservableArray async',
    async(inject([DataService], (dataService: DataService) => {
      mockBackend.connections.subscribe(
        (connection: MockConnection) => {
          connection.mockRespond(new Response(
            new ResponseOptions({
              body: [Observable, Observable]
            }
            )));
        });
      dataService.getChar().subscribe(
        (response) => {
          expect(response.length).toBe(2);
          expect(response[0]).toBe(Observable); <<<<<<<<<<<<<< Fails
          expect(response[1]).toBe(Observable); <<<<<<<<<<<<<< Fails
          expect(response).toEqual([Observable, Observable]); <<<<<< Fails
        });
    })));
});
Gabbro answered 1/11, 2017 at 8:44 Comment(6)
You might have misunderstood how forkJoin works. It does not return observables, it returns the actual results from each of its component observables. But having said that I still am not familiar enough with testing to provide an answer.Karyogamy
Can you provide a minimal git repo for debugging?Fluoro
yes i can give me a minGabbro
git hub link postedGabbro
you got the code?Gabbro
did you need some specific filesGabbro
Q
1

First of all, as @Aviad P. pointed, forkJoin method doesn't returns an Observable array of observables... it returns an array of the result of every observable in the forkJoin, and the result of that observables are not Observables instances.

Also, you are not mocking the getChart() method, with the mock backend you are mocking every http call, but not the method getChar() itself. The array must be length === 2 because not this.chartId and this.comicsId are present...

So I would say that the returned structure is something like that, so response[0] is an array:

response = [[Observable, Observable], [Observable, Observable]]

Said that, this expect will never be true, because no one array would be equal to a new array created:

expect(response).toEqual([Observable, Observable])

Modifying all that should resolve your problems. Also, In case your Observables returned and Observable instance, your code: ' body: [Observable, Observable] ' is not returning Observables instances, it is returning the Observable definition function, this would be a not correct mock, although the test would pass.

This would be a test example for ones is failing:

const mockResponse = {isMockResponse: true};

it('should get ObservableArray async',
async(inject([DataService], (dataService: DataService) => {
  mockBackend.connections.subscribe(
    (connection: MockConnection) => {
      connection.mockRespond(new Response(
        new ResponseOptions({
          body: {...mockResponse}
        }
        )));
    });
  dataService.getChar().subscribe(
    (response) => {
      expect(response.length).toBe(2);
      expect(response[0].isMockResponse).toBe(true); <<< Is this expect really necessary?
      expect(response[1].isMockResponse).toBe(true); <<< Is this expect really necessary?
    });
})));

Said that, The only expect that test your use case is this ones:

expect(response.length).toBe(2);

The other ones are unnecessary... so you are not testing the data you are mocking, you want to test the number of calls that has been performed due to the value of the variables this.chartId and this.comicsIs.

Hope this helps.

Quadrivalent answered 7/11, 2017 at 10:53 Comment(3)
can you write the test without it i wont be able to run itGabbro
I don't understand. What are you meaning? @GabbroIgorot
I edited my post, tell me if this is what you are expecting @GabbroIgorot

© 2022 - 2024 — McMap. All rights reserved.