1
votes

I have an Angular 2 service connected to the Angular Http service with Rxjs 5 to connect to a restful web service. The getObjects call simply returns the parsed Json in the form of an Observable array of meaningful objects. I've been trying to get the observable returned to resolve with my mocked Http response, but I haven't really found a working answer to this.

Test code:

import { getTestBed } from '@angular/core/testing';
import { MockBackend } from '@angular/http/testing';
import { TestScheduler } from "rxjs";

import { expect } from 'chai';
import { spy } from 'sinon';
import TestingUtilities from "../shared/test.utilities";

import Service from './service';
import ReturnObject from "../returnobject";

describe(`ServiceTests`, () => {
  let MOCK_DATA: string = ...mocked JSON string response...;

  let service: Service
  let backend: MockBackend
  let scheduler: TestScheduler

  function assertDeepEqualFrame(actual:any, expected:any) {
    console.log("test");
    if (!expected === actual) {
      throw new Error('Frames not equal!');
    }
  }

  beforeEach(() => {
    TestingUtilities.configureTestingModuleForMockHttp(getTestBed(), function () {
      return Service
    });

    backend = getTestBed().get(MockBackend);
    service = getTestBed().get(EarthquakeService);
    scheduler = new TestScheduler(assertDeepEqualFrame);
  });

  it('should return mocked data', () => {
    TestingUtilities.mockHttpResponse(backend, MOCK_DATA);

    let observables = service.getObjects();
    scheduler.expectObservable(observables).toBe("", functionToCreateMockObjects());
  });

The TestingUtilities is just a convenience wrapper around the solution for mocking the Http service provided by Angular presented at https://semaphoreci.com/community/tutorials/testing-angular-2-http-services-with-jasmine. This code above compiles, but it doesn't actually seem to return the mocked Observables nor assert on anything. I'm struggling to see exactly how the TestScheduler should be used to call an existing service and get observables back for validation. Anyone have any ideas?

2

2 Answers

1
votes

I do not use the TestScheduler to test observables. But I really like the following approach that I use:

import {TestBed, inject} from '@angular/core/testing';
import {BaseRequestOptions, Http, HttpModule, ResponseOptions, Response} from '@angular/http';
import {MockBackend} from '@angular/http/testing';
import {Book} from '../custom-types/book';
import {GoogleBooksService, API_PATH_SINGLE_BOOK} from './google-books.service';

const mockedHttpProvider = {
    provide: Http,
    deps: [MockBackend, BaseRequestOptions],
    useFactory: (backend: MockBackend, defaultOptions: BaseRequestOptions) => {
        return new Http(backend, defaultOptions);
    }
};

describe('Service: GoogleBooks', () => {
    beforeEach(() => {
        TestBed.configureTestingModule({
            imports: [HttpModule],
            providers: [
                GoogleBooksService,
                BaseRequestOptions,
                MockBackend,
                mockedHttpProvider
            ],
        });
    });

    it('should call the google books api',
        inject([GoogleBooksService, MockBackend], (service: GoogleBooksService, backend: MockBackend) => {
            let queryId: string = "someId";
            let expectedResponse: Book = {
                description: 'It's just Angular',
                title: 'How to test Observables'
            };

            backend.connections.subscribe(connection => {
                expect(connection.request.url).toBe(API_PATH_SINGLE_BOOK + queryId);
                let response = new ResponseOptions({body: JSON.stringify(expectedResponse)});
                connection.mockRespond(new Response(response));
            });

            service.getBookByGoogleBookId(queryId).subscribe(response => {
                expect(response).toEqual(expectedResponse);
            })
        })
    );
});

Service implementation:

@Injectable()
export class GoogleBooksService {

    constructor(private http: Http) {
    }

    getBookByGoogleBookId(id: string): Observable<Book> {
        return this.http.get(API_PATH + id)
            .map(res => res.json());
    }
}
0
votes

Try testing your service with a mocked backend this way:

describe(`ServiceTests`, () => {
  let MOCK_DATA: string = ...mocked JSON string response...;

  let earthquakeService: EarthquakeService;

  beforeEach(() => {
    TestBed.configureTestingModule( {
        providers: [
            EarthquakeService,
            { provide: XHRBackend, useClass: MockBackend },
            { provide: ComponentFixtureAutoDetect, useValue: true }
        ],
        imports: [
            HttpModule
        ]
    } )
    .compileComponents();
    });
  });

  it('should return mocked data', async( inject( [ Http, XHRBackend ], ( http: Http, backend: MockBackend ) => {
    backend.connections.subscribe( ( c: MockConnection ) => c.mockRespond( response ) );

    let options = new ResponseOptions( { status: 200, body: { data: MOCK_DATA } } );
    response = new Response( options );

    earthquakeService = new EarthquakeService( http );

    earthquakeService.getObjects().subscribe( results => {
        expect( results ).toEqual( { data: MOCK_DATA } );
    } ););

This tests your service in isolation. Testing other components which use this service can be done by extending this frame, or creating a stubbed service in those tests.