简体   繁体   English

angular2 测试,我如何模拟子组件

[英]angular2 test, how do I mock sub component

How do I mock sub component in jasmine tests?如何在 jasmine 测试中模拟子组件?

I have MyComponent , which uses MyNavbarComponent and MyToolbarComponent我有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 {}

When I test this component, I do not want to load and test those two sub components;当我测试这个组件时,我不想加载和测试这两个子组件; MyNavbarComponent, MyToolbarComponent, so I want to mock it. MyNavbarComponent,MyToolbarComponent,所以我想模拟它。

I know how to mock with services using provide(MyService, useClass(...)) , but I have no idea how to mock directives;我知道如何使用provide(MyService, useClass(...))来模拟服务,但我不知道如何模拟指令; components;成分;

  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');
    });
  });

Here is my plunker example;这是我的 plunker 示例;

http://plnkr.co/edit/q1l1y8?p=preview http://plnkr.co/edit/q1l1y8?p=preview

As requested, I'm posting another answer about how to mock sub components with input / output :根据要求,我发布了另一个关于如何使用input / output模拟子组件的答案:

So Lets start by saying we have TaskListComponent that displays tasks, and refreshes whenever one of them is clicked:因此,让我们首先说我们有TaskListComponent显示任务,并在单击其中之一时刷新:

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

app-task is a sub component with the [task] input and the (click) output. app-task是一个带有[task]输入和(click)输出的子组件。

Ok great, now we want to write tests for my TaskListComponent and of course we don't want to test the real app-task component.好的,现在我们想为我的TaskListComponent编写测试,当然我们不想测试真正的app-task组件。

so as @Klas suggested we can configure our TestModule with:所以@Klas 建议我们可以使用以下命令配置我们的TestModule

schemas: [CUSTOM_ELEMENTS_SCHEMA]

We might not get any errors at either build or runtime, but we won't be able to test much other than the existence of the sub component.我们可能不会在构建或运行时收到任何错误,但除了子组件的存在之外,我们将无法进行更多测试。

So how can we mock sub components?那么我们如何模拟子组件呢?

First we'll define a mock directive for our sub component (same selector):首先,我们将为我们的子组件(同一个选择器)定义一个模拟指令:

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

Now we'll declare it in the testing module:现在我们将在测试模块中声明它:

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;
});
  • Notice that because the generation of sub component of the fixture is happening asynchronously after its creation, we activate its autoDetectChanges feature.请注意,因为夹具的子组件的生成在其创建后异步发生,所以我们激活了它的 autoDetectChanges 功能。

In our tests, we can now query for the directive, access its DebugElement's injector, and get our mock directive instance through it:在我们的测试中,我们现在可以查询指令,访问其 DebugElement 的注入器,并通过它获取我们的模拟指令实例:

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

[This part should usually be in the beforeEach section, for cleaner code.] [这部分通常应该在beforeEach部分中,以获得更清晰的代码。]

From here, the tests are a piece of cake :)从这里开始,测试是小菜一碟:)

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();
});

If you use schemas: [CUSTOM_ELEMENTS_SCHEMA] in TestBed the component under test will not load sub components.如果您在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');
  }));

});

This works in the released version of Angular 2.0.这适用于 Angular 2.0 的发布版本。 Full code sample here . 完整的代码示例在这里

An alternative to CUSTOM_ELEMENTS_SCHEMA is NO_ERRORS_SCHEMA CUSTOM_ELEMENTS_SCHEMA的替代方案是NO_ERRORS_SCHEMA

Thanks to Eric Martinez, I found this solution.感谢 Eric Martinez,我找到了这个解决方案。

We can use overrideDirective function which is documented here, https://angular.io/docs/ts/latest/api/testing/TestComponentBuilder-class.html我们可以使用这里记录的overrideDirective函数, https://angular.io/docs/ts/latest/api/testing/TestComponentBuilder-class.html

It takes three prarmeters;它需要三个参数; 1. Component to implement 2. Child component to override 3. Mock component 1. 要实现的组件 2. 要覆盖的子组件 3. Mock 组件

Resolved solution is here at http://plnkr.co/edit/a71wxC?p=preview已解决的解决方案位于http://plnkr.co/edit/a71wxC?p=preview

This is the code example from the plunker这是来自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');    
  });
});

I put together a simple MockComponent module to help make this a little easier:我组合了一个简单的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);
    ...
  });

  ...
});

It's available at https://www.npmjs.com/package/ng2-mock-component .它可以在https://www.npmjs.com/package/ng2-mock-component 上找到

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM