2016-12-20 10 views
17

W jaki sposób kpisz z komponentu potomnego podczas testowania? Mam składnik nadrzędny o nazwie product-selected którego szablon wygląda tak:Przedszkole Komponenty dla dzieci - Angular 2

<section id="selected-container" class="container-fluid"> 
    <hr/> 
    <product-settings></product-settings> 
    <product-editor></product-editor> 
    <product-options></product-options> 
</section> 

i deklaracji komponent wygląda następująco:

import { Component, Input }    from '@angular/core'; 

import { ProductSettingsComponent } from '../settings/product-settings.component';          
import { ProductEditorComponent } from '../editor/product-editor.component';          
import { ProductOptionsComponent } from '../options/product-options.component';           

@Component({ 
    selector: 'product-selected', 
    templateUrl: './product-selected.component.html', 
    styleUrls: ['./product-selected.component.scss'] 
}) 
export class ProductSelectedComponent {} 

Ten składnik jest naprawdę tylko miejsce na inne składniki do życia i prawdopodobnie nie będzie zawierać żadnych innych funkcji.

Ale kiedy skonfigurować badań pojawia się następujący błąd szablon, powtarza się dla wszystkich trzech komponentów:

Error: Template parse errors: 
    'product-editor' is not a known element: 
    1. If 'product-editor' is an Angular component, then verify that it is part of this module. 
    2. If 'product-editor' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message. (" 
     <hr/> 
     <product-settings></product-settings> 
     [ERROR ->]<product-editor></product-editor> 

Próbowałem załadować szydzili wersję składników podrzędnych, ale nie wiem jak do zrobienia - przykłady, które widziałem, tylko przesłoniły rodzica i nawet nie wspominają o komponentach potomnych. Więc jak mam to zrobić?

Odpowiedz

13

Ogólnie rzecz biorąc, jeśli w widoku komponentu, który testujesz, używany jest komponent i nie chcesz go zadeklarować, ponieważ mogą one mieć własne zależności, aby uniknąć ** coś nie jest znanym elementem: ** błąd powinieneś użyć NO_ERRORS_SCHEMA.

import { NO_ERRORS_SCHEMA }   from '@angular/core'; 

TestBed.configureTestingModule({ 
     declarations: declarations, 
     providers: providers 
     schemas:  [ NO_ERRORS_SCHEMA ] 
    }) 

podstawie docs:

Dodaj NO_ERRORS_SCHEMA do schematów metadanych modułu testera, aby poinformować kompilator ignorować nierozpoznane elementy i atrybuty. Nie musisz już deklarować nieistotnych komponentów i dyrektyw.

Więcej na ten temat: https://angular.io/docs/ts/latest/guide/testing.html#!#shallow-component-test

+1

Wiesz, że byłem na tej stronie testowej, szukając testowania Observables i nigdy nie zauważyłem tej części. Dzięki - to była dobra lektura, choć szkoda, że ​​nie znalazłem jej wcześniej !! Zaakceptowanie twojej odpowiedzi, ponieważ jest o wiele prostsze niż moje – Katana24

+1

jest to bardzo złe rozwiązanie, ponieważ zaciemnia inne potencjalne problemy i dlatego jest sprzeczne z celem testowania kodu. Arnaud P dał dobrą odpowiedź. – user5080246

4

Wysłałem to pytanie, więc mogłem opublikować odpowiedź, gdy walczyłem z tym przez dzień lub dwa. Oto jak to zrobić:

let declarations = [ 
    ProductSelectedComponent, 
    ProductSettingsComponent, 
    ProductEditorComponent, 
    ProductOptionsComponent 
]; 

beforeEach(() => { 
     TestBed.configureTestingModule({ 
      declarations: declarations, 
      providers: providers 
     }) 
     .overrideComponent(ProductSettingsComponent, { 
      set: { 
       selector: 'product-settings', 
       template: `<h6>Product Settings</h6>` 
      } 
     }) 
     .overrideComponent(ProductEditorComponent, { 
      set: { 
       selector: 'product-editor', 
       template: `<h6>Product Editor</h6>` 
      } 
     }) 
     .overrideComponent(ProductOptionsComponent, { 
      set: { 
       selector: 'product-options', 
       template: `<h6>Product Options</h6>` 
      } 
     }); 

     fixture = TestBed.createComponent(ProductSelectedComponent); 
     cmp = fixture.componentInstance; 
     de = fixture.debugElement.query(By.css('section')); 
     el = de.nativeElement; 
    }); 

Musisz łańcucha funkcję overrideComponent dla każdego z elementów podrzędnych.

+0

Myślę, że pierwszy błąd nie jest ustalona przez nadrzędne, to po prostu potrzebne, aby zadeklarować te elementy, które zrobiłeś w dowolny sposób, tak nadrzędnym jest bezcelowe, chyba że go potrzebujesz – Milad

+0

@Milad to prawda ale w moim przypadku, którego nie uwzględniłem, jest to, że te komponenty mają swoje własne zależności od usług, które następnie należałoby uwzględnić dla nich. Ponieważ zależy mi tylko na testowaniu tego komponentu i nie dbam o innych poza bardzo podstawowym poziomem tworzenia, mogę z radością go zastąpić. – Katana24

25

Staranne o NO_ERRORS_SCHEMA. Przytoczmy kolejną część tego samego dokumentu:

Testy płytkich komponentów z NO_ERRORS_SCHEMA znacznie upraszczają testowanie jednostkowe złożonych szablonów. Jednak kompilator nie ostrzega już o błędach, takich jak błędne lub niewłaściwe użycie komponentów i dyrektyw.

Uważam, że ta wada jest całkiem sprzeczna z celami napisania testu. Tym bardziej, że nie jest trudno wyśmiać podstawowy komponent.

Podejście jeszcze nie wspomniano tutaj jest po prostu zadeklarować je w czasie config:

@Component({ 
    selector: 'product-settings', 
    template: '<p>Mock Product Settings Component</p>' 
}) 
class MockProductSettingsComponent {} 

@Component({ 
    selector: 'product-editor', 
    template: '<p>Mock Product Editor Component</p>' 
}) 
class MockProductEditorComponent {} 

... // third one 

beforeEach(() => { 
    TestBed.configureTestingModule({ 
     declarations: [ 
     ProductSelectedComponent, 
     MockProductSettingsComponent, 
     MockProductEditorComponent, 
     // ... third one 
     ], 
     providers: [/* your providers */] 
    }); 
    // ... carry on 
}); 
+0

Używam czegoś podobnego do testowania komponentów potomnych za pomocą Input and Outputs, a to jest świetne do sprawdzenia, czy wiązania działają w obie strony, jednak jest podatne na zgniliznę bitów, jako zmianę nazwy właściwości Input w "rzeczywistym" komponent potomny nie spowoduje niepowodzenia testu. Zastanawiam się, czy możliwe jest wygenerowanie dynamicznego typu Mock opartego na rzeczywistym elemencie potomnym, po prostu sprawdzając jego adnotacje. Ponieważ każdy składnik podrzędny może mieć nieznany zestaw zastrzyków zależności, musiałby być czymś opartym tylko na typie, a nie instancjonowaną wersją komponentu potomnego – Soraz

+0

W skrócie, myślę, że to, co musisz zrobić w tym przypadku, to jednostka -test również komponent potomny. Dokumenty mają [część poświęconą testowaniu wejść i wyjść] (https://angular.io/guide/testing#test-a-component-with-inputs-and-outputs), gdzie możesz mieć nadzieję, że znajdziesz to, czego potrzebujesz. Jeśli dobrze rozumiem, prawdopodobnie spodoba Ci się również [część, która bezpośrednio podąża] (https://angular.io/guide/testing#test-a-component-inside-a-test-host-component). –

+0

Cóż, przykład idzie w drugą stronę. Jak przetestować komponenty wiążące się z rodzica. Nie pomaga, jeśli masz komponent macierzysty z 4 komponentami potomnymi i chcesz przetestować komponent nadrzędny w izolacji, ponieważ nadal musisz ręcznie rozwiązać i podać wszystkie zależności komponentów potomnych lub ręcznie zachować wersję próbną każdy element potomny. Idealnie mógłbybym podzielić komponent na dwie części, dyrektywę bazową, która ma dane wejściowe i wyjściowe oraz dziedziczony składnik wersji, który zapewnia szablon, ale to nie zadziała, ponieważ musiałby mieć ten sam selektor. – Soraz

0

Znaleziono prawie idealne rozwiązanie, które będzie również prawidłowo rzucać błędy jeśli ktoś refactors komponent.

npm install mock-component 

Teraz w swoim .spec.ts można zrobić

import { MockComponent } from 'mock-component'; 
import { ChildComponent } from './child.component.ts'; 
... 
    beforeEach(
async(() => { 
    TestBed.configureTestingModule({ 
    imports: [FormsModule, ReactiveFormsModule, RouterTestingModule], 
    declarations: [ 
     ComponentUnderTest, 
     MockComponent(ChildComponent), 
     ... 

ta tworzy nowy anonimowy składnik, który ma ten sam przełącznik, @Input() i @Output() właściwości ChildComponent, ale bez żadnego kodu.

Załóżmy, że ChildComponent ma @Input() childValue: number, który jest związany z komponentu badanego, <app-child-component [childValue]="inputValue" />

Jedynym minusem znalazłem do tej pory, jest to, że nie można używać By.directive(ChildComponent) w badaniach, jak typ zmian, ale zamiast tego można użyć By.css('app-child-component') jak tak

it('sets the right value on the child component`,()=> { 
    component.inputValue=5; 
    fixture.detectChanges(); 
    const element = fixture.debugElement.query(By.css('app-child-component')); 
    expect(element).toBeTruthy(); 

    const child: ChildComponent = element.componentInstance; 

    expect(child.childValue).toBe(5); 
});