test angular2, comment simuler un sous-composant

Comment puis-je simuler un sous-élément dans les tests jasmine?

j'ai MyComponent, qui utilise MyNavbarComponent et MyToolbarComponent

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 {}

quand je teste ce composant, Je ne veux pas charger et tester ces deux sous-composants; MyNavbarComponent, MyToolbarComponent, donc je veux le moquer.

je sais comment faire pour se moquer avec les services de l'aide provide(MyService, useClass(...)), mais je n'ai aucune idée de la façon de se moquer de directives; composants;

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

Voici mon plunker exemple;

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

46
demandé sur allenhwkim 2016-03-14 00:06:22

4 réponses

Comme demandé, je poste une autre réponse sur la façon de se moquer de sous-composants input/output:

commençons donc par dire que nous avons TaskListComponent qui affiche les tâches, et se rafraîchit chaque fois que l'une d'elles est cliquée:

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

app-task est un sous-élément [task] et (click) sortie.

Ok super, maintenant nous voulons écrire des tests pour mon TaskListComponent et bien sûr, nous ne voulons pas de test réel app-taskcomposant.

comme @Klas nous a suggéré de configurer notre TestModule avec:

schemas: [CUSTOM_ELEMENTS_SCHEMA]

il se peut que nous n'ayons pas d'erreurs à la compilation ou à l'exécution, mais nous ne pourrons pas tester autre chose que l'existence du sous-composant.

alors comment pouvons-nous simuler des sous-composants?

nous allons d'abord définir une directive mock pour notre sous-composant (même sélecteur):

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

Maintenant, nous allons déclarer dans le test 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;
});
  • notez que puisque la génération du sous-composant de l'appareil se produit de façon asynchrone après sa création, nous activons sa fonctionnalité autoDetectChanges.

dans nos tests, nous pouvons maintenant interroger la directive, accéder à L'injecteur de son Débugelement, et obtenir notre instance de la directive mock à travers elle:

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

[Cette partie doit être habituellement beforeEach section, propre code.]

à Partir d'ici, les tests sont un morceau de gâteau :)

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();
});
52
répondu baryo 2017-07-26 11:45:14

Si vous utilisez schemas: [CUSTOM_ELEMENTS_SCHEMA]TestBed le composant à l'essai ne charge pas les sous-composants.

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

});

cela fonctionne dans la version publiée D'Angular 2.0. exemple de code Complet ici.

une alternative à CUSTOM_ELEMENTS_SCHEMANO_ERRORS_SCHEMA

21
répondu Klas Mellbourn 2016-10-09 18:45:26

grâce à Eric Martinez, j'ai trouvé cette solution.

nous pouvons utiliser overrideDirective fonction qui est documentée ici, https://angular.io/docs/ts/latest/api/testing/TestComponentBuilder-class.html

Résolu solution est ici à http://plnkr.co/edit/a71wxC?p=preview

ceci est l'exemple de code de la 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');    
  });
});
7
répondu allenhwkim 2016-07-13 13:45:04

j'ai mis en place un simple MockComponent module pour aider à rendre cela un peu plus facile:

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

  ...
});

il est disponible à https://www.npmjs.com/package/ng2-mock-component.

5
répondu Christian Nunciato 2017-01-13 18:04:05