2

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?

rasx
  • 5,288
  • 2
  • 45
  • 60

1 Answers1

1

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.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • the corrected test is here: https://github.com/BryanWilhite/angular.io-index-app/blob/master/index-app/src/app/services/songhay-blog-entries.service.spec.ts – rasx Nov 23 '17 at 04:04
  • Http provider is redundant and can be omitted if If HttpModule has been imported. – Estus Flask Nov 23 '17 at 08:30
  • I'll take a look at the [Plunker](http://plnkr.co/edit/9xEB6myPEtXMmLYMXpHu?p=preview) you recommended and work my way toward my scenario as simply removing references to `Http` breaks things in ways I currently do not understand. – rasx Nov 23 '17 at 21:10