angular2testing,我怎么模仿子组件

如何在茉莉花testing中模拟子组件?

我有MyComponent ,它使用MyNavbarComponentMyToolbarComponent

 import {Component} from 'angular2/core'; import {MyNavbarComponent} from './my-navbar.component'; import {MyToolbarComponent} from './my-toolbar.component'; @Component({ selector: 'my-app', template: ` <my-toolbar></my-toolbar> {{foo}} <my-navbar></my-navbar> `, directives: [MyNavbarComponent, MyToolbarComponent] }) export class MyComponent {} 

当我testing这个组件时,我不想加载和testing这两个子组件; MyNavbarComponent,MyToolbarComponent,所以我想嘲笑它。

我知道如何使用provide(MyService, useClass(...))来模拟服务,但我不知道如何模拟指令; 组件;

  beforeEach(() => { setBaseTestProviders( TEST_BROWSER_PLATFORM_PROVIDERS, TEST_BROWSER_APPLICATION_PROVIDERS ); //TODO: want to mock unnecessary directives for this component test // which are MyNavbarComponent and MyToolbarComponent }) it('should bind to {{foo}}', injectAsync([TestComponentBuilder], (tcb) => { return tcb.createAsync(MyComponent).then((fixture) => { let DOM = fixture.nativeElement; let myComponent = fixture.componentInstance; myComponent.foo = 'FOO'; fixture.detectChanges(); expect(DOM.innerHTML).toMatch('FOO'); }); }); 

这是我的掠夺者的例子。

根据要求,我发布了另一个关于如何模拟input / output子组件的答案:

因此,让我们开始说我们有TaskListComponent显示任务,并刷新时,只要其中一个被点击:

 <div id="task-list"> <div *ngFor="let task of (tasks$ | async)"> <app-task [task]="task" (click)="refresh()"></app-task> </div> </div> 

app-task[task]input和(click)输出的子组件。

好吧,现在我们要为我的TaskListComponent编写testing,当然我们不想testing真正的app-task组件。

所以@Klasbuild议我们可以configuration我们的TestModule

 schemas: [CUSTOM_ELEMENTS_SCHEMA] 

我们可能不会在构build或运行时遇到任何错误,但除了子组件的存在之外,我们将无法进行其他testing。

那么我们如何模拟子组件呢?

首先,我们将为我们的子组件(相同的select器)定义一个模拟指令:

 @Directive({ selector: 'app-task' }) class MockTaskDirective { @Input('task') public task: ITask; @Output('click') public clickEmitter = new EventEmitter<void>(); } 

现在我们将在testing模块中声明它:

 let fixture : ComponentFixture<TaskListComponent>; let cmp : TaskListComponent; beforeEach(() => { TestBed.configureTestingModule({ declarations: [TaskListComponent, **MockTaskDirective**], // schemas: [CUSTOM_ELEMENTS_SCHEMA], providers: [ { provide: TasksService, useClass: MockService } ] }); fixture = TestBed.createComponent(TaskListComponent); **fixture.autoDetectChanges();** cmp = fixture.componentInstance; }); 
  • 请注意,由于灯具的子组件的生成是在创build后asynchronous发生的,我们激活它的autoDetectChangesfunction。

在我们的testing中,我们现在可以查询指令,访问DebugElement的注入器,并通过它获得我们的mock指令实例:

 import { By } from '@angular/platform-browser'; const mockTaskEl = fixture.debugElement.query(By.directive(MockTaskDirective)); const mockTaskCmp = mockTaskEl.injector.get(MockTaskDirective) as MockTaskDirective; 

[这个部分通常应该在beforeEach节之前,为了更清晰的代码。]

从这里,testing是一块蛋糕:)

 it('should contain task component', ()=> { // Arrange. const mockTaskEl = fixture.debugElement.query(By.directive(MockTaskDirective)); // Assert. expect(mockTaskEl).toBeTruthy(); }); it('should pass down task object', ()=>{ // Arrange. const mockTaskEl = fixture.debugElement.query(By.directive(MockTaskDirective)); const mockTaskCmp = mockTaskEl.injector.get(MockTaskDirective) as MockTaskDirective; // Assert. expect(mockTaskCmp.task).toBeTruthy(); expect(mockTaskCmp.task.name).toBe('1'); }); it('should refresh when task is clicked', ()=> { // Arrange spyOn(cmp, 'refresh'); const mockTaskEl = fixture.debugElement.query(By.directive(MockTaskDirective)); const mockTaskCmp = mockTaskEl.injector.get(MockTaskDirective) as MockTaskDirective; // Act. mockTaskCmp.clickEmitter.emit(); // Assert. expect(cmp.refresh).toHaveBeenCalled(); }); 

如果在TestBed使用schemas: [CUSTOM_ELEMENTS_SCHEMA] ,则TestBed组件不会加载子组件。

 import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { TestBed, async } from '@angular/core/testing'; import { MyComponent } from './my.component'; describe('App', () => { beforeEach(() => { TestBed .configureTestingModule({ declarations: [ MyComponent ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }); }); it(`should have as title 'app works!'`, async(() => { let fixture = TestBed.createComponent(MyComponent); let app = fixture.debugElement.componentInstance; expect(app.title).toEqual('Todo List'); })); }); 

这适用于Angular 2.0的发布版本。 完整代码示例在这里 。

CUSTOM_ELEMENTS_SCHEMA的替代CUSTOM_ELEMENTS_SCHEMANO_ERRORS_SCHEMA

感谢Eric Martinez,我find了这个解决scheme。

我们可以使用这里logging的overrideDirective函数, https://angular.io/docs/ts/latest/api/testing/TestComponentBuilder-class.html

它需要三个prarmeters; 1.要实现的组件2.要覆盖的子组件3.模拟组件

已解决的解决scheme位于http://plnkr.co/edit/a71wxC?p=preview

这是来自plunker的代码示例

 import {MyNavbarComponent} from '../src/my-navbar.component'; import {MyToolbarComponent} from '../src/my-toolbar.component'; @Component({template:''}) class EmptyComponent{} describe('MyComponent', () => { beforeEach(injectAsync([TestComponentBuilder], (tcb) => { return tcb .overrideDirective(MyComponent, MyNavbarComponent, EmptyComponent) .overrideDirective(MyComponent, MyToolbarComponent, EmptyComponent) .createAsync(MyComponent) .then((componentFixture: ComponentFixture) => { this.fixture = componentFixture; }); )); it('should bind to {{foo}}', () => { let el = this.fixture.nativeElement; let myComponent = this.fixture.componentInstance; myComponent.foo = 'FOO'; fixture.detectChanges(); expect(el.innerHTML).toMatch('FOO'); }); }); 

我把一个简单的MockComponent模块放在一起,以帮助简化这个过程:

 import { TestBed } from '@angular/core/testing'; import { MyComponent } from './src/my.component'; import { MockComponent } from 'ng2-mock-component'; describe('MyComponent', () => { beforeEach(() => { TestBed.configureTestingModule({ declarations: [ MyComponent, MockComponent({ selector: 'my-subcomponent', inputs: ['someInput'], outputs: [ 'someOutput' ] }) ] }); let fixture = TestBed.createComponent(MyComponent); ... }); ... }); 

它可以在https://www.npmjs.com/package/ng2-mock-component