Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testing a service #130

Closed
sanderkoenders opened this issue Nov 12, 2015 · 3 comments
Closed

Testing a service #130

sanderkoenders opened this issue Nov 12, 2015 · 3 comments
Labels

Comments

@sanderkoenders
Copy link

Hello,

We've been trying to test a service which depends on the angular HTTP service but we can't seem to get this to work properly.
My code is as follows:

app.spec.ts:

/// <reference path="../../src/typings/_custom.d.ts" />

// Import necessary wrappers for Jasmine
import {
    beforeEachProviders,
    describe,
    expect,
    iit,
    inject,
    it,
    injectAsync,
    fakeAsync,
    TestComponentBuilder,
    tick
} from 'angular2/testing';
import { Component, provide} from 'angular2/angular2';
import {MockBackend, BaseRequestOptions, Http} from 'angular2/http';

// Load the implementations that should be tested
import { App } from '../../src/app/app';
import {InstrumentFactory} from "../../src/app/Factories/InstrumentFactory";

describe('App', () => {
    // provide our implementations or mocks to the dependency injector
    beforeEachProviders(() => [
        App,
        BaseRequestOptions,
        MockBackend,
        provide(Http, {useFactory:
            function(backend, defaultOptions) {
                return new Http(backend, defaultOptions);
            },
            deps: [MockBackend, BaseRequestOptions]})
    ]);

    it('should have a title', inject([Http], (http) => {
        var instrumentFactory = new InstrumentFactory(http);
        var instrument = instrumentFactory.create({"id" : 7});

        expect(instrument).not.toBeUndefined();
    }));

    it('should also be able to test', () => {
        expect(true).toBe(true);
    });

});

InstrumentFactory.ts:

import {Injectable} from 'angular2/angular2';
import {Http} from 'angular2/http';
import {Instrument} from "../Models/Instrument";

@Injectable()
export class InstrumentFactory {
    private http : Http;

    public constructor(http : Http) {
        this.http = http;
    }

    public create(instrument:any) : Instrument {
        return new Instrument(instrument);
    }

    public all() {
        return this.http.get('http://127.0.0.1:3001/api/Instruments');
    }

    //public find(query : string) : Instrument {
    //    return query;
    //}
}

I am getting the folowing error:

karma start
ts-loader: Using typescript@1.6.2 and /home/sander/Documents/Projects/ZZG/angular2/tsconfig.json
12 11 2015 13:19:58.488:INFO [karma]: Karma v0.13.15 server started at http://localhost:9876/
12 11 2015 13:19:58.491:WARN [launcher]: Can not load "phantomjs", it is not registered!
  Perhaps you are missing some plugin?
12 11 2015 13:19:58.507:INFO [launcher]: Starting browser Chrome
12 11 2015 13:19:58.520:INFO [launcher]: Starting browser Firefox
12 11 2015 13:19:59.724:INFO [Chrome 46.0.2490 (Linux 0.0.0)]: Connected on socket KF4eXpYGQF6kqO5IAAAA with id 52653721
Chrome 46.0.2490 (Linux 0.0.0) App should have a title FAILED
    TypeError: Cannot read property 'getXHR' of undefined
        at _getAppBindings (/home/sander/Documents/Projects/ZZG/angular2/spec.bundle.js:5621:63 <- webpack:///~/angular2/src/testing/test_injector.js:91:33)
        at Object.createTestInjector (/home/sander/Documents/Projects/ZZG/angular2/spec.bundle.js:5631:80 <- webpack:///~/angular2/src/testing/test_injector.js:101:0)
        at Object.<anonymous> (/home/sander/Documents/Projects/ZZG/angular2/spec.bundle.js:5067:49 <- webpack:///~/angular2/src/testing/testing.js:82:0)
Chrome 46.0.2490 (Linux 0.0.0): Executed 2 of 2 (1 FAILED) (0.187 secs / 0.024 secs)
Chrome 46.0.2490 (Linux 0.0.0): Executed 2 of 2 (1 FAILED) (0.187 secs / 0.024 secs)
Firefox 42.0.0 (Ubuntu 0.0.0): Executed 0 of 2 SUCCESS (0 secs / 0 secs)
12 11 2015 13:20:01.840:WARN [reporter]: SourceMap position not found for trace: TypeError: dom_adapter_1.DOM is undefined in http://localhost:9876/base/spec.bundle.js?75d1a9b50de9d20c93a1d29fceae00b422b18b66 (line 5621)
_getAppBindings@http://localhost:9876/base/spec.bundle.js?75d1a9b50de9d20c93a1d29fceae00b422b18b66:5621:36
Firefox 42.0.0 (Ubuntu 0.0.0) App should have a title FAILED
    TypeError: dom_adapter_1.DOM is undefined in /home/sander/Documents/Projects/ZZG/angular2/spec.bundle.js (line 5621)
    _getAppBindings@/home/sander/Documents/Projects/ZZG/angular2/spec.bundle.js:5621:36 <- webpack:///~/angular2/src/testing/test_injector.js:91:33
    createTestInjector@/home/sander/Documents/Projects/ZZG/angular2/spec.bundle.js:5631:80 <- webpack:///~/angular2/src/testing/test_injector.js:101:0
    _it/<@/home/sander/Documents/Projects/ZZG/angular2/spec.bundle.js:5067:33 <- webpack:///~/angular2/src/testing/testing.js:82:0
Chrome 46.0.2490 (Linux 0.0.0): Executed 2 of 2 (1 FAILED) (0.187 secs / 0.024 secs)
Firefox 42.0.0 (Ubuntu 0.0.0): Executed 2 of 2 (1 FAILED) (0.003 secs / 0.032 secs)
TOTAL: 2 FAILED, 2 SUCCESS

I know that phantomjs is not defined, but that should not really matter to this error.

@dotcs
Copy link
Contributor

dotcs commented Nov 23, 2015

Seems related to #124.

Cannot read property 'getXHR' of undefined

This should have been fixed by selecting the correct BrowserDomAdapter. Currently this is done in spec.bundle.js which will be included in Karma. See https://github.com/AngularClass/angular2-webpack-starter/blob/master/spec.bundle.js#L34-L37. I will have a closer look to the problem.

@dotcs
Copy link
Contributor

dotcs commented Nov 23, 2015

For me it works in the following way:

// app.spec.ts
describe('App', () => {
  // provide our implementations or mocks to the dependency injector
  beforeEachProviders(() => [
    App,
    InstrumentFactory,
    BaseRequestOptions,
    MockBackend,
    provide(Http, {useFactory:
      function(backend, defaultOptions) {
        return new Http(backend, defaultOptions);
      },
      deps: [MockBackend, BaseRequestOptions]})
  ]);

  it('should have an instrument', inject([InstrumentFactory], (instrumentFactory => {
    var instrument = instrumentFactory.create({"id" : 7});
    expect(instrument).not.toBeUndefined();
  })));

});
// InstrumentFactory.ts
import {Injectable} from 'angular2/angular2';
import {Http} from 'angular2/http';
//import {Instrument} from "../Models/Instrument";   // you would use this import instead of the Instrument class below

/* Simple class for testing purposes only */
class Instrument {
  private name: string;

  public constructor(options: {id: number}) {
    this.name = 'MyInstrument';
  }
}

@Injectable()
export class InstrumentFactory {
  private http : Http;

  public constructor(http : Http) {
    this.http = http;
  }

  public create(instrument:any): Instrument {
    console.log(this.http);  // just to demonstrate in the test, that the mocked HTTP backend is available via dependency injection
    return new Instrument(instrument);
  }

}

So the trick is not to manually inject HTTP into the InstrumentFactory, but provide access to let the Dependency Injector know about the InstrumentFactory. The injection of the HTTP service is then done automatically (and implicit) by the Dependency Injector when a new instance of the HTTP service is required.

Why is this better? Think of a large app with hundreds of possible injections. What you want to do is tell the injector what should be used whenever a dependency is requested (e.g. use a mocked version of the HTTP service, etc.). Then whenever the injector resolves a dependency it chooses whatever you have defined. This makes it much easier than resolving the dependencies by hand, which is what you have tried in your approach.

So please give it a try if it does work in this way. I have added a demo on my fork of this project.

@dotcs dotcs added the question label Nov 23, 2015
@PatrickJS
Copy link
Owner

I'm adding this to the readme so I'll close this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants