Angular2单元测试使用@Input()

我有一个在实例变量上使用 @Input()注释的组件,我正在尝试为 openProductPage()方法编写单元测试,但是我对如何设置单元测试有点迷惑。我把这个实例变量公之于众,但我认为我不应该诉诸于此。

如何设置 Jasmine 测试以便注入(提供?)模拟的产品我可以测试 openProductPage()的方法吗?

我的组成部分:

import {Component, Input} from "angular2/core";
import {Router} from "angular2/router";


import {Product} from "../models/Product";


@Component({
selector: "product-thumbnail",
templateUrl: "app/components/product-thumbnail/product-thumbnail.html"
})


export class ProductThumbnail {
@Input() private product: Product;




constructor(private router: Router) {
}


public openProductPage() {
let id: string = this.product.id;
this.router.navigate([“ProductPage”, {id: id}]);
}
}
105689 次浏览

You need to set the product value on the component instance after it has been loaded within your test.

As a sample here is a simple component within an input that you can use as a foundation for your use case:

@Component({
selector: 'dropdown',
directives: [NgClass],
template: `
<div [ngClass]="{open: open}">
</div>
`,
})
export class DropdownComponent {
@Input('open') open: boolean = false;


ngOnChanges() {
console.log(this.open);
}
}

And the corresponding test:

it('should open', injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
return tcb.createAsync(DropdownComponent)
.then(fixture => {
let el = fixture.nativeElement;
let comp: DropdownComponent = fixture.componentInstance;


expect(el.className).toEqual('');


// Update the input
comp.open = true; // <-----------


// Apply
fixture.detectChanges(); // <-----------


var div = fixture.nativeElement.querySelector('div');
// Test elements that depend on the input
expect(div.className).toEqual('open');
});
}));

See this plunkr as a sample: https://plnkr.co/edit/YAVD4s?p=preview.

I usually do something like:

describe('ProductThumbnail', ()=> {
it('should work',
injectAsync([ TestComponentBuilder ], (tcb: TestComponentBuilder) => {
return tcb.createAsync(TestCmpWrapper).then(rootCmp => {
let cmpInstance: ProductThumbnail =
<ProductThumbnail>rootCmp.debugElement.children[ 0 ].componentInstance;


expect(cmpInstance.openProductPage()).toBe(/* whatever */)
});
}));
}


@Component({
selector  : 'test-cmp',
template  : '<product-thumbnail [product]="mockProduct"></product-thumbnail>',
directives: [ ProductThumbnail ]
})
class TestCmpWrapper {
mockProduct = new Product(); //mock your input
}

Note that product and any other fields on the ProductThumbnail class can be private with this approach (which is the main reason I prefer it over Thierry's approach, despite the fact that it's a little more verbose).

If you use TestBed.configureTestingModule to compile your test component, here's another approach. It's basically the same as the accepted answer, but may be more similar to how angular-cli generates the specs. FWIW.

import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DebugElement } from '@angular/core';


describe('ProductThumbnail', () => {
let component: ProductThumbnail;
let fixture: ComponentFixture<TestComponentWrapper>;


beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [
TestComponentWrapper,
ProductThumbnail
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
.compileComponents();


fixture = TestBed.createComponent(TestComponentWrapper);
component = fixture.debugElement.children[0].componentInstance;
fixture.detectChanges();
});


it('should create', () => {
expect(component).toBeTruthy();
});
});


@Component({
selector: 'test-component-wrapper',
template: '<product-thumbnail [product]="product"></product-thumbnail>'
})
class TestComponentWrapper {
product = new Product()
}

this is from official documentation https://angular.io/docs/ts/latest/guide/testing.html#!#component-fixture. So you can create new input object expectedHero and pass it to the component comp.hero = expectedHero

Also make sure to call fixture.detectChanges(); last, otherwise property will not be bound to component.

Working Example

// async beforeEach
beforeEach( async(() => {
TestBed.configureTestingModule({
declarations: [ DashboardHeroComponent ],
})
.compileComponents(); // compile template and css
}));


// synchronous beforeEach
beforeEach(() => {
fixture = TestBed.createComponent(DashboardHeroComponent);
comp    = fixture.componentInstance;
heroEl  = fixture.debugElement.query(By.css('.hero')); // find hero element


// pretend that it was wired to something that supplied a hero
expectedHero = new Hero(42, 'Test Name');
comp.hero = expectedHero;
fixture.detectChanges(); // trigger initial data binding
});