it-swarm.com.de

angle2 test, wie mache ich sub component mock

Wie verspotte ich eine Unterkomponente in Jasmin-Tests?

Ich habe MyComponent, das MyNavbarComponent und MyToolbarComponent verwendet

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

Wenn ich diese Komponente teste, möchte ich diese beiden Unterkomponenten nicht laden und testen. MyNavbarComponent, MyToolbarComponent, also möchte ich es verspotten.

Ich weiß, wie man mit Diensten spottet, die provide(MyService, useClass(...)) verwenden, aber ich habe keine Ahnung, wie man Direktiven spottet. Komponenten;

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

Hier ist mein Plunker-Beispiel;

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

50
allenhwkim

Wie gewünscht, veröffentliche ich eine weitere Antwort zum Verspotten von Unterkomponenten mit input/output:

Beginnen wir also damit, dass wir TaskListComponent haben, das Aufgaben anzeigt und aktualisiert, wenn auf eine davon geklickt wird:

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

app-task Ist eine Unterkomponente mit den Eingaben [task] Und (click).

Ok, großartig, jetzt wollen wir Tests für mein TaskListComponent schreiben und natürlich wollen wir nicht die echte app-task - Komponente testen.

so wie @Klas vorgeschlagen hat, können wir unser TestModule konfigurieren mit:

schemas: [CUSTOM_ELEMENTS_SCHEMA]

Wir erhalten möglicherweise weder zur Erstellung noch zur Laufzeit Fehler, können jedoch nur die Existenz der Unterkomponente testen.

Wie können wir Subkomponenten verspotten?

Zuerst definieren wir eine Mock-Direktive für unsere Unterkomponente (gleicher Selektor):

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

Jetzt deklarieren wir es im Testmodul:

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;
});
  • Beachten Sie, dass wir die Funktion autoDetectChanges aktivieren, da die Erzeugung der Unterkomponente des Scheinwerfers nach seiner Erstellung asynchron erfolgt.

In unseren Tests können wir jetzt die Direktive abfragen, auf den Injector seines DebugElement zugreifen und unsere Mock-Direktiven-Instanz durch sie erhalten:

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

[Dieser Teil sollte normalerweise im Abschnitt beforeEach enthalten sein, um den Code übersichtlicher zu gestalten.]

Ab hier sind die Tests ein Kinderspiel :)

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

Wenn du benutzt schemas: [CUSTOM_ELEMENTS_SCHEMA]in TestBed lädt die zu testende Komponente keine Unterkomponenten.

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

});

Dies funktioniert in der veröffentlichten Version von Angular 2.0. Vollständiges Codebeispiel hier .

Eine Alternative zu CUSTOM_ELEMENTS_SCHEMA ist NO_ERRORS_SCHEMA

23
Klas Mellbourn

Dank Eric Martinez habe ich diese Lösung gefunden.

Wir können die Funktion overrideDirective verwenden, die hier dokumentiert ist https://angular.io/docs/ts/latest/api/testing/TestComponentBuilder-class.html

Es sind drei Prarmeter erforderlich. 1. Zu implementierende Komponente 2. Zu überschreibende untergeordnete Komponente 3. Scheinkomponente

Die gelöste Lösung finden Sie hier unter http://plnkr.co/edit/a71wxC?p=preview

Dies ist das Codebeispiel aus dem 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
allenhwkim

Ich habe ein einfaches MockComponent Modul zusammengestellt, um dies ein wenig einfacher zu machen:

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

  ...
});

Es ist verfügbar unter https://www.npmjs.com/package/ng2-mock-component .

5