TestComponentBuilder可以测试组件中的角度组件吗?

时间:2016-03-16 20:45:58

标签: angularjs unit-testing angular karma-jasmine angular2-template

所以,我正在尝试为我的组件编写单元测试。我的组件是动态创建的列表,当用户单击某些项目时,我希望我的测试验证组件是否正确生成输出。

为了测试这个,我编写了一个演示组件,在其模板中创建我的组件,然后尝试让TestComponentBuilder创建这个演示组件并单击列表中的项目。不幸的是,我的单元测试用例无法找到我的组件的html元素来点击它们。我可以找到演示组件的元素,但似乎测试无法访问我的嵌套组件。

我的问题是: 我可以使用这种方法吗? 或者是否有更好的方法来对我的组件的输入和输出进行单元测试?

任何帮助将不胜感激。这就是我正在做的事情:

< ==根据Gunter的答案更新==>

CustomListComponent.ts(我的组件):

import {Component, EventEmitter} from "angular2/core";
import {CustomListItem} from "./CustomListItem";

@Component({
    selector: 'custom-list-component',
    inputs: ['itemClass', 'items'],
    outputs: ['onChange'],
    template: `
    <ul class="list-unstyled">
      <li *ngFor="#item of items" [hidden]="item.hidden">
        <div [class]="itemClass" (click)="onItemClicked(item)">
            {{item.text}}
        </div>
      </li>
    </ul>
    `
})
export class CustomListComponent {
    itemClass: String;
    items: CustomListItem[];
    onChange: EventEmitter<CustomListItem>;

    constructor() {
        this.items = new Array<CustomListItem>();
        this.onChange = new EventEmitter();
    }

    onItemClicked(item: CustomListItem): void {
        let clone = new CustomListItem(item.text, item.hidden);
        this.onChange.emit(clone);
    }
}

CustomListItem.ts:

export class CustomListItem {
    text: String;
    hidden: boolean;

    constructor(text: String, hidden: boolean) {
        this.text = text;
        this.hidden = hidden;
    }
}

CustomListSpec.ts(我在这里挣扎的地方):

import {CustomListComponent} from './CustomListComponent';
import {ComponentFixture, describe, expect, fakeAsync, inject, injectAsync, it, TestComponentBuilder} from 'angular2/testing'
import {Component} from "angular2/core";
import {CustomListItem} from "./CustomListItem";
import {By} from "angular2/src/platform/dom/debug/by";

describe('CustomListComponent', () => {
    var el;
    var dropdownToggleBtn, dropdownListEl;
    var regularListEl;

    it('Event output properly when list item clicked', injectAsync([TestComponentBuilder], (tcb) => {
        return createComponent(tcb).then((fixture) => {
            console.log("el (" + el + ")"); // => [object HTMLDivElement]
            console.log("dropdownToggleBtn (" + dropdownToggleBtn + ")"); // => [object HTMLButtonElement]
            console.log("dropdownListContainer(" + dropdownListContainer + ")"); // => [object HTMLDivElement]
            console.log("dropdownListEl(" + dropdownListEl + ")"); // => [object HTMLUListElement]
            //console.log("regularListEl (" + regularListEl+ ")");

            //...further testing...
        });
    }));

    function createComponent(tcb: TestComponentBuilder): Promise<ComponentFixture> {
        return tcb.createAsync(CustomListComponentDemo).then((fixture) => {
            fixture.detectChanges();

            el = fixture.debugElement.nativeElement;
            dropdownToggleBtn = fixture.debugElement.query(By.css(".btn")).nativeElement;
            dropdownListContainer = fixture.debugElement.query(By.css(".dropdown-menu")).nativeElement;
            dropdownListEl = dropdownListContainer.children[0].children[0];
            //regularListEl = fixture.debugElement.query(By.css(".list-unstyled")); //TIMES OUT unfortunately. Maybe because there are 2.

            return fixture;
        })
    }
});

@Component({
    directives: [CustomListComponent],
    template: `
        <div class="btn-group" dropdown>
            <button id="dropdown-toggle-id" type="button" class="btn btn-light-gray" dropdownToggle>
                <i class="glyphicon icon-recent_activity dark-green"></i> Dropdown <span class="caret"></span>
            </button>
            <div class="dropdown-menu" role="menu" aria-labelledby="dropdown-toggle-id">
            <custom-list-component id="dropdown-list-id"
                 [items]="dropdownListItems" [itemClass]="'dropdown-item'"
                 (onChange)="onDropdownListChange($event)">
            </custom-list-component>
        </div>
        <span class="divider">&nbsp;</span>
    </div>
    <custom-list-component id="regular-list-id"
        [items]="regularListItems"
        (onChange)="onRegularListChange($event)">
    </custom-list-component>
    `
})
class CustomListComponentDemo {
    dropdownListItems: CustomListItem[];
    regularListItems: CustomListItem[];

    constructor() {
        //intialize the item lists
    }

    onDropdownListChange(item: CustomListItem): void {
        //print something to the console logs
    }

    onRegularListChange(item: CustomListItem): void {
        //print something to the console logs
    }
}

1 个答案:

答案 0 :(得分:1)

在查询动态创建的元素之前调用detectChanges()。在更改检测运行之前不会创建它们。

function createComponent(tcb: TestComponentBuilder): Promise<ComponentFixture> {
    return tcb.createAsync(CustomListComponentDemo).then((fixture) => {
        fixture.detectChanges();
        el = fixture.debugElement.nativeElement;
        regularListEl = fixture.debugElement.query(By.css(".list-unstyled"));
        dropdownListEl = fixture.debugElement.query(By.css(".dropdown-menu")).nativeElement;
        dropdownToggleBtn = fixture.debugElement.query(By.css(".btn")).nativeElement;

        return fixture;
    })
}