Подтвердить что ты не робот

Mocking Child Components - Angular 2

Как вы издеваетесь над дочерним компонентом при тестировании? У меня есть родительский компонент с именем product-selected, шаблон которого выглядит так:

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

И объявление компонента выглядит следующим образом:

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

Этот компонент - это просто место, где другие компоненты могут жить и, вероятно, не будут содержать никаких других функций.

Но когда я настраиваю тестирование, я получаю следующую ошибку шаблона, повторяющуюся для всех трех компонентов:

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>

Я пытался загрузить издевательскую версию дочерних компонентов, но не знаю, как это сделать - примеры, которые я видел, просто переопределяют родителя и даже не упоминают дочерние компоненты. Итак, как мне это сделать?

4b9b3361

Ответ 1

Осторожно о NO_ERRORS_SCHEMA. Позвольте процитировать другую часть того же документа:

Неглубокие тесты компонентов с помощью NO_ERRORS_SCHEMA значительно упрощают модульное тестирование сложных шаблонов. Однако компилятор больше не предупреждает вас о таких ошибках, как неправильные или неправильно используемые компоненты и директивы.

Я нахожу этот недостаток совершенно противоположным целям написания теста. Тем более, что это не так сложно издеваться над базовым компонентом.

Подход, еще не упомянутый здесь, - это просто объявить их во время конфигурации:

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

Ответ 2

Нашел почти идеальное решение, которое также будет корректно выдавать ошибки, если кто-то реорганизует компонент.

npm install --only=dev ng-mocks

Теперь в вашем .spec.ts вы можете сделать

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

Это создает новый анонимный компонент, который имеет те же свойства селектора, свойства @Input() и @Output() объекта ChildComponent, но без прикрепленного кода.

Предположим, что ваш ChildComponent имеет @Input() childValue: number, которое связано с тестируемым компонентом, <app-child-component [childValue]="inputValue"/>

Единственный недостаток, который я обнаружил до сих пор, это то, что вы не можете использовать By.directive(ChildComponent) в своих тестах при изменении типа, но вместо этого вы можете использовать By.css('app-child-component') как так

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

Ответ 3

Как правило, если у вас есть компонент, используемый в представлении компонента, который вы тестируете, и вы не хотите объявлять этот компонент, потому что у них могут быть свои собственные зависимости, чтобы избежать ** чего-то не является известным элементом: ** ошибка вы должны использовать NO_ERRORS_SCHEMA.

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

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

На основе документов:

Добавьте NO_ERRORS_SCHEMA в метаданные схем модулей тестирования, чтобы сообщить компилятору игнорировать непризнанные элементы и атрибуты. Вам больше не нужно объявлять несущественные компоненты и директивы.

Подробнее об этом: https://angular.io/docs/ts/latest/guide/testing.html#!#shallow-component-test

Ответ 4

Я разместил этот вопрос, чтобы опубликовать ответ, когда я боролся с этим в течение дня или двух. Вот как вы это делаете:

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

Вам нужно связать функцию overrideComponent для каждого из дочерних компонентов.