使用 @Input() 进行 Angular2 单元测试
Angular2 unit test with @Input()
我有一个在实例变量上使用 @Input()
注释的组件,我正在尝试为 openProductPage()
方法编写单元测试,但我有点迷茫关于我如何设置我的单元测试。我 可以 使该实例变量 public,但我认为我不应该诉诸于此。
如何设置我的 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}]);
}
}
在测试中加载组件实例后,您需要在组件实例上设置 product
值。
这里的示例是输入中的一个简单组件,您可以将其用作用例的基础:
@Component({
selector: 'dropdown',
directives: [NgClass],
template: `
<div [ngClass]="{open: open}">
</div>
`,
})
export class DropdownComponent {
@Input('open') open: boolean = false;
ngOnChanges() {
console.log(this.open);
}
}
以及相应的测试:
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');
});
}));
将此 plunkr 视为示例:https://plnkr.co/edit/YAVD4s?p=preview。
我通常会这样做:
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
}
请注意 product
和 ProductThumbnail
class 上的任何其他字段可以 使用这种方法是私有的(这是主要原因我更喜欢它而不是 Thierry 的方法,尽管它有点冗长)。
如果您使用 TestBed.configureTestingModule
来编译您的测试组件,这是另一种方法。它与公认的答案基本相同,但可能更类似于 angular-cli 生成规范的方式。 FWIW.
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { DebugElement } from '@angular/core';
describe('ProductThumbnail', () => {
let component: ProductThumbnail;
let fixture: ComponentFixture<TestComponentWrapper>;
beforeEach(async(() => {
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()
}
这来自官方文档https://angular.io/docs/ts/latest/guide/testing.html#!#component-fixture。所以你可以创建新的输入对象 expectedHero 并将其传递给组件 comp.hero = expectedHero
还要确保最后调用fixture.detectChanges();
,否则属性将不会绑定到组件。
工作示例
// 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
});
我有一个在实例变量上使用 @Input()
注释的组件,我正在尝试为 openProductPage()
方法编写单元测试,但我有点迷茫关于我如何设置我的单元测试。我 可以 使该实例变量 public,但我认为我不应该诉诸于此。
如何设置我的 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}]);
}
}
在测试中加载组件实例后,您需要在组件实例上设置 product
值。
这里的示例是输入中的一个简单组件,您可以将其用作用例的基础:
@Component({
selector: 'dropdown',
directives: [NgClass],
template: `
<div [ngClass]="{open: open}">
</div>
`,
})
export class DropdownComponent {
@Input('open') open: boolean = false;
ngOnChanges() {
console.log(this.open);
}
}
以及相应的测试:
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');
});
}));
将此 plunkr 视为示例:https://plnkr.co/edit/YAVD4s?p=preview。
我通常会这样做:
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
}
请注意 product
和 ProductThumbnail
class 上的任何其他字段可以 使用这种方法是私有的(这是主要原因我更喜欢它而不是 Thierry 的方法,尽管它有点冗长)。
如果您使用 TestBed.configureTestingModule
来编译您的测试组件,这是另一种方法。它与公认的答案基本相同,但可能更类似于 angular-cli 生成规范的方式。 FWIW.
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { DebugElement } from '@angular/core';
describe('ProductThumbnail', () => {
let component: ProductThumbnail;
let fixture: ComponentFixture<TestComponentWrapper>;
beforeEach(async(() => {
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()
}
这来自官方文档https://angular.io/docs/ts/latest/guide/testing.html#!#component-fixture。所以你可以创建新的输入对象 expectedHero 并将其传递给组件 comp.hero = expectedHero
还要确保最后调用fixture.detectChanges();
,否则属性将不会绑定到组件。
工作示例
// 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
});