Using Jasmine to test an Angular Service that loads a static JSON file

Viewed 823

I have been slightly diverging from the guidance in Testing Services in Angular 2 to test this method, loadIndex():

loadIndex(): Promise<Response> {
    this.index = null;
    this.isLoaded = false;
    this.isLoading = true;

    const promise = this.client
        .get('./src/assets/data/index.json') // TODO: inject this from config?
        .toPromise();
    promise
        .catch(() => {
            this.isError = true;
            this.isLoaded = false;
        })
        .then(responseOrVoid => {
            const response = <Response>responseOrVoid;
            if (!response) {
                return;
            }

            this.index = response.json().data as Array<BlogEntry>;
            if (!this.index) {
                return;
            }

            this.isLoaded = true;
            this.isLoading = false;
        });

    return promise;
}

with this test:

import { TestBed, async, getTestBed } from '@angular/core/testing';
import {
    BaseRequestOptions,
    Http,
    Response,
    ResponseOptions,
    XHRBackend
} from '@angular/http';
import { BlogEntriesService } from './songhay-blog-entries.service';
import { BlogEntry } from '../models/songhay-blog-entry';
import { error } from 'util';

describe('BlogEntriesService', () => {
    const testBed = getTestBed();
    let service: BlogEntriesService;

    beforeEach(() => {
        TestBed.configureTestingModule({
            providers: [
                BaseRequestOptions,
                BlogEntriesService,
                {
                    deps: [BaseRequestOptions],
                    provide: Http,
                    useFactory: (
                        backend: XHRBackend,
                        defaultOptions: BaseRequestOptions
                    ) => new Http(backend, defaultOptions)
                }
            ]
        });
    });

    it('should load index', done => {
        service = testBed.get(BlogEntriesService);
        expect(service).toBeDefined();
        expect(service).toBeTruthy();
        service
            .loadIndex()
            .then(result => {
                expect(result).toBeDefined();
            })
            .catch(result => expect(result).toBeUndefined())
            .then(done);
    });
});

I have removed the MockBackend code from my test because I think I do not need it because I am loading a local, static JSON file (not really a unit test). So the error I am getting is this:

TypeError: Cannot read property 'merge' of undefined

at mergeOptions (http://localhost:9876/_karma_webpack_/webpack:/../angular.io-index-app/index-app/node_modules/@angular/http/@angular/http.es5.js:1824:1)
    at Http.webpackJsonp.../../../http/@angular/http.es5.js.Http.get (http://localhost:9876/_karma_webpack_/webpack:/../angular.io-index-app/index-app/node_modules/@angular/http/@angular/http.es5.js:1924:1)
    at BlogEntriesService.webpackJsonp.../../../../../src/app/services/songhay-blog-entries.service.ts.BlogEntriesService.loadIndex (http://localhost:9876/_karma_webpack_/webpack:/../angular.io-index-app/index-app/src/app/services/songhay-blog-entries.service.ts:78:14)
    at Object.<anonymous> (http://localhost:9876/_karma_webpack_/webpack:/../angular.io-index-app/index-app/src/app/services/songhay-blog-entries.service.spec.ts:39:14)
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9876/_karma_webpack_/webpack:/../angular.io-index-app/index-app/node_modules/zone.js/dist/zone.js:392:1)
    at ProxyZoneSpec.webpackJsonp.../../../../zone.js/dist/proxy.js.ProxyZoneSpec.onInvoke (http://localhost:9876/_karma_webpack_/webpack:/../angular.io-index-app/index-app/node_modules/zone.js/dist/proxy.js:79:1)
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9876/_karma_webpack_/webpack:/../angular.io-index-app/index-app/node_modules/zone.js/dist/zone.js:391:1)
    at Zone.webpackJsonp.../../../../zone.js/dist/zone.js.Zone.run (http://localhost:9876/_karma_webpack_/webpack:/../angular.io-index-app/index-app/node_modules/zone.js/dist/zone.js:142:1)
    at Object.<anonymous> (http://localhost:9876/_karma_webpack_/webpack:/../angular.io-index-app/index-app/node_modules/zone.js/dist/jasmine-patch.js:102:1)
    at attemptAsync (http://localhost:9876/base/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?da99c5b057693d025fad3d7685e1590600ca376d:3945:24)

Is this issue here known? Should I just give up and build an e2e test?

1 Answers

The code has DI problems. Http factory has one dependency injected:

deps: [BaseRequestOptions],

While the factory expects 2 parameters, and they are mixed up.

Instead, it should be:

{
    deps: [XHRBackend, BaseRequestOptions],
    provide: Http,
    useFactory: (
        backend: XHRBackend,
        defaultOptions: BaseRequestOptions
    ) => new Http(backend, defaultOptions)
}

The only reason why Http provider is defined this way is that because usually the backend is replaced with MockBackend in unit tests. If this is not the case for integration test, HttpModule can be just imported instead.

Related