Tests unitaires sur un composant Angular utilisant @Input() et @Output()
Nombreux sont les développeurs Angular qui ont peur d'écrire des tests unitaires ou ne savent carrément pas comment faire et par où commencer. Le but de cet article est de prendre un léger exemple afin de démystifier la complexité autour des tests unitaires. Nous utiliserons bien-sûr le combo Jasmine/Karma
qui est déjà présent lors de la création d'un projet Angular.
En effet, Jasmine
est un framework qui nous permet d'aller vite dans l'écriture de nos tests unitaires et Karma
est un serveur qui nous permet de lancer nos tests avec un rendu de tout ce qui se passe sur notre navigateur.
Considérons un composant réutilisable
HelloComponent
qui permet d'afficher le nom de l'utilisateur (username
) dès qu'on clique sur un bouton (Hello
).
<div>
<p> {{ username }} </p>
<button (click)='sayHello(username)'> Hello </button>
</div>
export class HelloComponent {
@Input() username = '';
@Output() hello = new EventEmitter<string>()
public sayHello(username: string): void {
this.hello.emit(username);
}
}
Comme nous pouvons le pouvoir, nous avons notre composant HelloComponent
qui comprend :
username
qui est notre propriétéInput()
hello
qui est notre évènementOutput()
Notre composant réutilisable pourra être appelé par un composant parent comme ceci par exemple :
<app-hello [username]="'john'" (hello)="onSayHello($event)">
</app-hello>
En créant un composant Angular, nous avons généralement un fichier qui a pour extension `.spec.ts`
. C'est le fichier dédié à l'écriture des tests unitaires liés à notre composant. En ouvrant le fichier hello.component.spec.ts
qui fait référence à notre composant HelloComponent, nous avons ce code de base :
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NotificationToastComponent } from './hello.component';
describe('HelloComponent', () => {
let component: HelloComponent;
let fixture: ComponentFixture<HelloComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ HelloComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(HelloComponent);
component = fixture.componentInstance;
component.username = 'john';
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
Le code généré nous a créé une instance du composant
HelloComponent
. Ensuite, il a fait appel à la méthodedetectChanges()
pour mettre à jour la vue si entre temps une propriété du composant a été modifié.Nous avons rajouté
component.username = 'john';
afin qu'il initialise la valeur deusername
avec'john'
lors de la création de l'instance.
Nous pouvons à présent écrire les tests unitaires qu'il faut par rapport à notre logique métier. Nous écrirons deux tests unitaires :
1) Le premier test vise à vérifier que notre variable username
est bien affichée sur notre interface :
it('should display username', () => {
const messageElement = fixture.nativeElement.querySelector('p');
expect(messageElement.textContent).toContain('john');
});
- Dans notre premier test ci-dessus, nous avons sélectionné l'élément
HTML
qui affiche la propriétéusername
et vérifié qu'il contient la valeur"john"
. La valeur deusername
a été initialisée dans la méthodebeforeEach()
.
2) Le second test vise à vérifier que l'évènement hello()
est émise lorsqu'on fait appel à la méthode sayHello()
:
it('should emit hello event', () => {
spyOn(component.hello, 'emit');
component.sayHello('john');
expect(component.hello.emit).toHaveBeenCalledWith('john');
});
- Dans notre second test ci-dessus, nous avons utilisé la méthode
spyOn()
pour espionner l'évènementhello()
. En effet,spyOn()
est une fonction qui enregistre des informations sur les appels de la méthode à espionner, comme le nombre de fois où elle a été appelée, avec quels arguments et ce qu'elle a renvoyé. Enfin, nous avons vérifié que l'événementhello()
a été émis avec la valeur'john'
lorsqu'on appelle la fonctionsayHello()
.
Nous pouvons lancer la commande
ng test
pour lancer le serveurKarma
et voir le rendu de nos tests unitaires sur un navigateur.
Pour finir, voici le code complet lié aux tests unitaires sur notre composant HelloComponent
:
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NotificationToastComponent } from './hello.component';
describe('HelloComponent', () => {
let component: HelloComponent;
let fixture: ComponentFixture<HelloComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ HelloComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(HelloComponent);
component = fixture.componentInstance;
component.username = 'john';
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should display username', () => {
const messageElement = fixture.nativeElement.querySelector('p');
expect(messageElement.textContent).toContain('john');
});
it('should emit hello event', () => {
spyOn(component.hello, 'emit');
component.sayHello('john');
expect(component.hello.emit).toHaveBeenCalledWith('john');
});
});
En somme, nous avons exploré comment écrire des tests unitaires pour un composant Angular qui utilise les décorateurs @Input()
et @Output()
. Les tests unitaires sont une partie importante du processus de développement logiciel et nous espérons que nous les aborderons davantage avec plus d'aisance et moins de peur.
Pour plus d'informations :