
Angular 2 testing
Jesús Rodríguez
Consultor en IdeaBlade

Agenda
- Un pequeño repaso...
- TestBed.
- UNIT testing y mocks.
- Testeando componente.
- Testeando servicio.
- Testeando tubería.
- Comentarios en ejemplo real.

Un pequeño repaso...
En Angular tenemos los NgModule:
@NgModule({
imports: [ BrowserModule, HttpModule ],
declarations: [ AppComponent, BetComponent, CapitalizePipe ],
providers: [ TeamService ],
bootstrap: [ AppComponent ]
})
export AppModule {}Que es como una caja donde registramos las distintas piezas de nuestra aplicación...

TestBed
Y en testing, tenemos nuestros propios NgModule:
TestBed.configureTestingModule({
imports: [ ... ],
declarations: [ ... ],
providers: [ ... ],
...
})Nuestra propia caja vacía que podemos configurar como necesitemos.
TestBed es la utilidad principal de Angular para escribir nuestros tests.

UNIT testing y mocks
La regla de oro de los unit test, es que una unidad (unit) tiene que ser testeada sin necesitar ninguna de sus dependencias.
class TeamServiceSpy {
getTeams = jasmine.createSpy('getTeams').and.callFake(() => {
return Observable.of([{ id: 0, name: 'Madrid'}, { id: 1, name: 'Malaga' }]);
});
}Un ejemplo de mock de un servicio sería:
Eso quiere decir, que si una unidad tiene dependencias, tenemos que reemplazarlas por mocks.

Testeando componente
Tenemos un componente TeamsComponent que inyecta TeamService como dependencia y muestra un listado de equipos:
@Component({
selector: 'gr-teams',
template: `
<ul>
<li *ngFor="let team of teams">
{{team.id}} - {{team.name}}
</li>
</ul>
`
})
export class TeamsComponent implements OnInit {
teams: any;
constructor(private teamService: TeamService) { }
ngOnInit() {
this.teamService.getTeams().subscribe(teams => this.teams = teams);
}
}
Testeando componente
Lo primordial es coger ese módulo, esa caja vacía y darle lo mínimo que nos hace falta, TeamsComponent y un mock de TeamService.
TestBed.configureTestingModule({
declarations: [ TeamsComponent ],
providers: [ ¿ ? ]
});¿Qué ponemos en provider? No podemos poner el original y si le damos el mock no sabrá para qué.
providers: [{ provide: TeamService, useClass: TeamServiceSpy }]
Testeando componente
Una vez tenemos el módulo configurado, creamos el componente:
let fixture: ComponentFixture<TeamsComponent>;
fixture = TestBed.createComponent(TeamsComponent);Y guardamos una referencia a la instancia en si:
let instance: TeamsComponent;
instance = fixture.componentInstance;Por último, vamos a pedirle al inyector la instancia del servicio:
let teamService: TeamService;
teamService = fixture.debugElement.injector.get(TeamService);
Testeando componente
Al ejecutar ngOnInit, ¿está recibiendo los equipos?
it('fetches all teams', () => {
instance.ngOnInit();
expect(instance.teams.length).toBe(2);
expect(teamService.getTeams).toHaveBeenCalled();
});¿Y los muestra por pantalla?
it('renders the teams on the screen', () => {
fixture.detectChanges();
const li = fixture.debugElement.queryAll(By.css('li'));
expect(li.length).toBe(2);
});
Testeando componente
Como alternativa, se puede usar un componente de prueba que haga de wrapper al componente bajo test.
@Component({
selector: 'gr-test',
template: '<gr-team [home]="home"></gr-team>'
})
class TestComponent {
home = { id: 0, name: 'Malaga' };
}Gracias a este wrapper, podríamos testear la API (entradas y salidas) de nuestro componente.

Testeando servicio
Supongamos que tenemos el siguiente servicio:
export class TeamService {
constructor(private http: Http) { }
getTeams() {
return this.http.get('api/teams')
.map((res) => res.json().data);
}
getTeam(id: number) {
return this.http.get(`api/teams/${id}`)
.map((res) => res.json().data);
}
}
Testeando servicio
¿Cómo hacemos mock de HTTP?
providers: [
TeamService,
{
provide: Http,
useFactory: (backend, options) => {
return new Http(backend, options);
},
deps: [MockBackend, BaseRequestOptions]
},
MockBackend,
BaseRequestOptions
]
Testeando servicio
Inyectando nuestras dependencias antes de cada test:
beforeEach(inject([TeamService, MockBackend],
(service: TeamService, backend: MockBackend) => {
showService = service;
mockBackend = backend;
fakeTeams = {
data: [
{ id: 0, name: 'Malaga' },
{ id: 1, name: 'Madrid' }
]
};
}));Y creando algunos equipos de ejemplo...

Testeando servicio
... que habría que proveer cuando hagan falta.
it('gets the list of shows', () => {
mockBackend.connections.subscribe((connection) => {
connection.mockRespond(new Response(new ResponseOptions({
body: JSON.stringify(fakeTeams)
})));
});
showService.getShows().subscribe((shows) => {
expect(shows.length).toBe(2);
expect(shows[0].name).toBe('Malaga');
expect(shows[1].name).toBe('Madrid');
});
});
Testeando tuberías
Y sin TestBed ;)
describe('CapitalizePipe', () => {
let pipe = new CapitalizePipe();
it('capitalizes "golden race"', () => {
expect(pipe.transform('golden race')).toBe('Golden race');
});
it('capitalizes the entire sentence', () => {
expect(pipe.transform('golden race', true)).toBe('Golden Race');
});
});import { Pipe, PipeTransform } from '@angular/core';
@Pipe({ name: 'capitalize'})
export class CapitalizePipe implements PipeTransform {
transform(value: string) {
return value[0].toUpperCase() + value.substring(1);
}
}
Usando by
Para testeo de componentes, tenemos acceso a By:
import { By } from '@angular/platform-browser';fixture.debugElement.query(By.css('foo'));
fixture.debugElement.queryAll(by.directive('directive'));
Angular 2 testing
By Jesus Rodriguez
Angular 2 testing
- 466