unit testing - test - prueba angular2, ¿cómo me burlo del componente secundario?
testing angular 2 (4)
Gracias a Eric Martinez, encontré esta solución.
Podemos usar la función overrideDirective
que está documentada aquí, https://angular.io/docs/ts/latest/api/testing/TestComponentBuilder-class.html
Toma tres piremetros; 1. Componente para implementar 2. Componente hijo para sobrescribir 3. Componente simulado
La solución resuelta está aquí en http://plnkr.co/edit/a71wxC?p=preview
Este es el ejemplo de código del 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'');
});
});
¿Cómo me burlo del componente secundario en las pruebas de jazmín?
Tengo MyComponent
, que usa MyNavbarComponent
y 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 {}
Cuando pruebo este componente, no quiero cargar y probar esos dos subcomponentes; MyNavbarComponent, MyToolbarComponent, así que quiero simularlo.
Sé cómo provide(MyService, useClass(...))
servicios con provide(MyService, useClass(...))
, pero no tengo idea de cómo simular las directivas; componentes;
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'');
});
});
Aquí está mi ejemplo de Plunker;
Según lo solicitado, publicaré otra respuesta sobre cómo simular subcomponentes con input
/ output
:
Así que comencemos diciendo que tenemos TaskListComponent
que muestra tareas y las actualiza cada vez que se hace clic en una de ellas:
<div id="task-list">
<div *ngFor="let task of (tasks$ | async)">
<app-task [task]="task" (click)="refresh()"></app-task>
</div>
</div>
app-task
es un subcomponente con la entrada [task]
y la salida (click)
.
Bien, ahora queremos escribir pruebas para TaskListComponent
y, por supuesto, no queremos probar el componente real de la app-task
.
así que @Klas sugirió que podemos configurar nuestro TestModule
con:
schemas: [CUSTOM_ELEMENTS_SCHEMA]
Es posible que no tengamos errores ni en la compilación ni en el tiempo de ejecución, pero no podremos probar mucho más que la existencia del subcomponente.
Entonces, ¿cómo podemos simular subcomponentes?
Primero definiremos una directiva simulada para nuestro subcomponente (mismo selector):
@Directive({
selector: ''app-task''
})
class MockTaskDirective {
@Input(''task'')
public task: ITask;
@Output(''click'')
public clickEmitter = new EventEmitter<void>();
}
Ahora lo declararemos en el módulo de prueba:
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;
});
- Tenga en cuenta que debido a que la generación del subcomponente del dispositivo está sucediendo de forma asíncrona después de su creación, activamos su función autoDetectChanges.
En nuestras pruebas, ahora podemos consultar la directiva, acceder a su inyector DebugElement y obtener nuestra instancia de directiva simulada a través de ella:
import { By } from ''@angular/platform-browser'';
const mockTaskEl = fixture.debugElement.query(By.directive(MockTaskDirective));
const mockTaskCmp = mockTaskEl.injector.get(MockTaskDirective) as MockTaskDirective;
[Esta parte generalmente debe estar en la sección beforeEach
, para obtener un código más limpio.]
A partir de aquí, las pruebas son pan comido :)
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();
});
Si usa schemas: [CUSTOM_ELEMENTS_SCHEMA]
en TestBed
el componente bajo prueba no cargará subcomponentes.
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'');
}));
});
Esto funciona en la versión lanzada de Angular 2.0. Muestra de código completo aquí .
Una alternativa a CUSTOM_ELEMENTS_SCHEMA
es NO_ERRORS_SCHEMA
MockComponent
módulo simple de MockComponent
para ayudar a hacer esto un poco más fácil:
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);
...
});
...
});
Está disponible en https://www.npmjs.com/package/ng2-mock-component .