web-dev-qa-db-fra.com

Test unitaire angulaire2 avec @Input ()

J'ai un composant qui utilise l'annotation @Input() sur une variable d'instance et j'essaie d'écrire mon test unitaire pour la méthode openProductPage(), mais je suis un peu perdu à comment j'ai configuré mon test unitaire. Je pourrait rendre cette instance variable publique, mais je ne pense pas que je devrais avoir recours à cela.

Comment configurer mon test Jasmine pour qu'un produit simulé soit injecté (fourni?) Et que je puisse tester la méthode openProductPage()?

Mon composant:

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}]);
    }
}
51
hartpdx

Je fais habituellement quelque chose comme:

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 
}

Notez que product et tous les autres champs de la classe ProductThumbnailpeut peuvent être privés avec cette approche (ce qui est la raison principale pour laquelle je la préfère à celle de Thierry, malgré le fait que c'est un peu plus verbeux).

14
awqueous

Si tu utilises TestBed.configureTestingModule pour compiler votre composant de test, voici une autre approche. C'est fondamentalement la même chose que la réponse acceptée, mais peut être plus similaire à la façon dont angular-cli génère les spécifications. 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()
}
39
Danny Bullis

ceci provient de la documentation officielle https://angular.io/docs/ts/latest/guide/testing.html#!#component-fixture . Ainsi, vous pouvez créer un nouvel objet d’entrée attenduHero et le transmettre au composant comp.hero = attendHero

Assurez-vous également d'appeler fixture.detectChanges(); last, sinon la propriété ne sera pas liée au composant.

Exemple de travail

// 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
});
28
Vazgen Manukyan

Vous devez définir la valeur product sur l'instance du composant après son chargement dans votre test.

À titre d'exemple, voici un composant simple d'une entrée que vous pouvez utiliser comme base pour votre cas d'utilisation:

@Component({
  selector: 'dropdown',
  directives: [NgClass],
  template: `
    <div [ngClass]="{open: open}">
    </div>
  `,
})
export class DropdownComponent {
  @Input('open') open: boolean = false;

  ngOnChanges() {
    console.log(this.open);
  }
}

Et le test correspondant:

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

Voir cet exemple comme exemple: https://plnkr.co/edit/YAVD4s?p=preview .

18
Thierry Templier