Тест angular2, как мне издеваться над подкомпонентом
Как мне макет подкомпонента в тестах жасмина?
я имею MyComponent
, который использует MyNavbarComponent
а также 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 {}
Когда я тестирую этот компонент, я не хочу загружать и тестировать эти два подкомпонента; MyNavbarComponent, MyToolbarComponent, поэтому я хочу издеваться над ним.
Я умею издеваться над сервисами используя provide(MyService, useClass(...))
, но я понятия не имею, как издеваться над директивами; компоненты;
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');
});
});
Вот мой пример плунжера;
4 ответа
В ответ на запрос я публикую еще один ответ о том, как макетировать субкомпоненты с помощью input
/output
:
Итак, давайте начнем с того, что мы имеем TaskListComponent
который отображает задачи и обновляет их при каждом нажатии на одну из них:
<div id="task-list">
<div *ngFor="let task of (tasks$ | async)">
<app-task [task]="task" (click)="refresh()"></app-task>
</div>
</div>
app-task
является субкомпонентом с [task]
вход и (click)
выход.
Хорошо, отлично, теперь мы хотим написать тесты для моего TaskListComponent
и, конечно, мы не хотим проверять реальное app-task
составная часть.
так как @Klas предложил, мы можем настроить наш TestModule
с:
schemas: [CUSTOM_ELEMENTS_SCHEMA]
Мы можем не получить никаких ошибок ни во время сборки, ни во время выполнения, но мы не сможем протестировать многое другое, кроме существования подкомпонента.
Итак, как мы можем издеваться над субкомпонентами?
Сначала мы определим директиву mock для нашего подкомпонента (тот же селектор):
@Directive({
selector: 'app-task'
})
class MockTaskDirective {
@Input('task')
public task: ITask;
@Output('click')
public clickEmitter = new EventEmitter<void>();
}
Теперь мы объявим это в модуле тестирования:
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;
});
- Обратите внимание, что, поскольку генерация подкомпонента прибора происходит асинхронно после его создания, мы активируем его функцию autoDetectChanges.
В наших тестах мы можем теперь запросить директиву, получить доступ к ее инжектору DebugElement и получить через нее наш экземпляр директивы mock:
import { By } from '@angular/platform-browser';
const mockTaskEl = fixture.debugElement.query(By.directive(MockTaskDirective));
const mockTaskCmp = mockTaskEl.injector.get(MockTaskDirective) as MockTaskDirective;
[Эта часть обычно должна быть в beforeEach
раздел, для более чистого кода.]
Отсюда тесты - кусок пирога:)
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();
});
Если вы используете schemas: [CUSTOM_ELEMENTS_SCHEMA]
в TestBed
тестируемый компонент не будет загружать субкомпоненты.
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');
}));
});
Это работает в выпущенной версии Angular 2.0. Полный пример кода здесь.
Альтернатива CUSTOM_ELEMENTS_SCHEMA
является NO_ERRORS_SCHEMA
Благодаря Эрику Мартинесу я нашел это решение.
Мы можем использовать overrideDirective
функция, которая описана здесь, https://angular.io/docs/ts/latest/api/testing/TestComponentBuilder-class.html
Требуется три параметра; 1. Компонент для реализации 2. Дочерний компонент для переопределения 3. Макет компонента
Решенное решение здесь на http://plnkr.co/edit/a71wxC?p=preview
Это пример кода от плунжера
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');
});
});
Я собрал простой MockComponent
модуль, который поможет сделать это немного проще:
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);
...
});
...
});
Он доступен по адресу https://www.npmjs.com/package/ng2-mock-component.