Angular2 - 在组件中测试 ngOninit

Posted

技术标签:

【中文标题】Angular2 - 在组件中测试 ngOninit【英文标题】:Angular2 - Testing ngOninit in Components 【发布时间】:2016-08-09 06:37:05 【问题描述】:

我有一个包含以下代码的列表组件:

///<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
                   );
  

我希望为ngOninit 钩子编写单元测试。我写了以下测试:

/// <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);
                );
        )
    );
);

但是,console.log在测试中的输出是一个空的ul标签如下:

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

谁能建议我为组件钩子编写单元测试的正确方法?

解决方案

模拟injectAsync块中的http请求如下:

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

                    var response = new Response(options);

                    connection.mockRespond(response);
                
            );

但是现在我收到另一个错误如下:

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.

【问题讨论】:

【参考方案1】:

如果您在 ngOnInit() 中调用异步代码,则不能假设在执行 console.log(...) 时它已完成。 this.employees 仅在您传递给 subscribe(...) 的回调在响应到达后被调用时设置。

如果您使用MockBackend,您可以控制响应,并且在响应通过后您必须再次运行fixture.detectChanges() 以使组件使用更新的数据重新渲染,然后您可以阅读innerHTML 并期待它包含呈现的内容。

【讨论】:

您的解决方案有效。但现在我收到以下错误。你能建议一个解决方法吗? 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. 也许***.com/questions/35011972/… 仍然遇到同样的错误。你能在这里添加一个示例代码吗?

以上是关于Angular2 - 在组件中测试 ngOninit的主要内容,如果未能解决你的问题,请参考以下文章

测试使用 setInterval 或 setTimeout 的 Angular2 组件

Angular 2 单元测试组件,模拟 ContentChildren

如何为 angular2 的登录组件编写测试(单元测试)

angular2测试,如何测试事件绑定到一个元素

Angular2 - 在测试中模拟 RouteParams

Angular 2 单元测试数据从父组件传递到子组件