Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2 - Testing ngOninit in Components

I have a listing component with following code:

///<reference path="../../node_modules/angular2/typings/browser.d.ts"/>
import { Component, OnInit } from 'angular2/core';
import { ROUTER_DIRECTIVES } from 'angular2/router';

import { Employee } from '../models/employee';
import { EmployeeListServiceComponent } from '../services/employee-list-service.component';

@Component({
  selector: 'employee-list',
  template: `
    <ul class="employees">
  <li *ngFor="#employee of employees">
    <a [routerLink]="['EmployeeDetail', {id: employee.id}]">
      <span class="badge">{{employee.id}}</span>
      {{employee.name}}
    </a>
  </li>
</ul>
  `,
  directives: [ROUTER_DIRECTIVES],
  providers: [EmployeeListServiceComponent]
})

export class EmployeeListComponent implements OnInit {
  public employees: Employee[];
  public errorMessage: string;

  constructor(
    private _listingService: EmployeeListServiceComponent
  ){}

  ngOnInit() {
    this._listingService.getEmployees().subscribe(
                     employees => this.employees = employees,
                     error =>  this.errorMessage = <any>error
                   );
  }
}

I wish to write unit tests for the ngOninit hook. I have written following test:

/// <reference path="../../typings/main/ambient/jasmine/jasmine.d.ts" />

import {
    it,
    describe,
    expect,
    TestComponentBuilder,
    injectAsync,
    setBaseTestProviders,
    beforeEachProviders,
} from "angular2/testing";
import { Component, provide, ApplicationRef, OnInit } from "angular2/core";
import {
    TEST_BROWSER_PLATFORM_PROVIDERS,
    TEST_BROWSER_APPLICATION_PROVIDERS
} from "angular2/platform/testing/browser";
import {
    ROUTER_DIRECTIVES,
    ROUTER_PROVIDERS,
    ROUTER_PRIMARY_COMPONENT,
    APP_BASE_HREF
} from 'angular2/router';
import {XHRBackend, HTTP_PROVIDERS} from "angular2/http";
import { MockApplicationRef } from 'angular2/src/mock/mock_application_ref';
import {MockBackend } from "angular2/src/http/backends/mock_backend";
import {Observable} from 'rxjs/Rx';
import 'rxjs/Rx';

import { Employee } from '../models/employee';
import { EmployeeListComponent } from './list.component';
import { EmployeeListServiceComponent } from '../services/employee-list-service.component';


class MockEmployeeListServiceComponent {

    getEmployees () {
        return Observable.of([
            {
                "id": 1,
                "name": "Abhinav Mishra"
            }
        ]);
    }
}


@Component({
    template: '<employee-list></employee-list>',
    directives: [EmployeeListComponent],
    providers: [MockEmployeeListServiceComponent]
})
class TestMyList {}


describe('Employee List Tests', () => {
    setBaseTestProviders(TEST_BROWSER_PLATFORM_PROVIDERS, TEST_BROWSER_APPLICATION_PROVIDERS);

    beforeEachProviders(() => {
        return [
            ROUTER_DIRECTIVES,
            ROUTER_PROVIDERS,
            HTTP_PROVIDERS,
            provide(EmployeeListServiceComponent, {useClass: MockEmployeeListServiceComponent}),
            provide(XHRBackend, {useClass: MockBackend}),
            provide(APP_BASE_HREF, {useValue: '/'}),
            provide(ROUTER_PRIMARY_COMPONENT, {useValue: EmployeeListComponent}),
            provide(ApplicationRef, {useClass: MockApplicationRef})
        ]
    });

    it('Should be true',
        injectAsync([TestComponentBuilder], (tcb) => {
            return tcb
                .createAsync(TestMyList)
                .then((fixture) => {
                    fixture.detectChanges();
                    var compiled = fixture.debugElement.nativeElement;
                    console.log(compiled.innerHTML);
                    expect(true).toBe(true);
                });
        })
    );
});

However, the output of console.log in the test is an empty ul tag as follows:

 '<employee-list>
    <ul class="employees">
  <!--template bindings={}-->
</ul>
  </employee-list>'

Can anyone suggest me the proper way of writing unit tests for component hooks?

SOLUTION

Mock the http request in the injectAsync block as follows:

backend.connections.subscribe(
                (connection:MockConnection) => {
                    var options = new ResponseOptions({
                        body: [
                            {
                                "id": 1,
                                "name": "Abhinav Mishra"
                            }
                        ]
                    });

                    var response = new Response(options);

                    connection.mockRespond(response);
                }
            );

However now i am getting another error as follows:

Failed: EXCEPTION: Component "EmployeeListComponent" has no route config. in [['EmployeeDetail', {id: employee.id}] in EmployeeListComponent@3:7]
        ORIGINAL EXCEPTION: Component "EmployeeListComponent" has no route config.
        ORIGINAL STACKTRACE:
        Error: Component "EmployeeListComponent" has no route config.
like image 903
abhinavmsra Avatar asked Apr 17 '16 15:04

abhinavmsra


1 Answers

If you call async code in ngOnInit() you can't assume it is completed when console.log(...) is executed. this.employees is only set when the callback you passed to subscribe(...) gets called after the response arrived.

If you use MockBackend you can control the response and after the response was passed you have to run fixture.detectChanges() again to make the component re-render with the updated data, then you can read innerHTML and expect it to contain the rendered content.

like image 155
Günter Zöchbauer Avatar answered Oct 21 '22 14:10

Günter Zöchbauer