Test unitaire événement de clic dans Angular
J'essaie d'ajouter des tests unitaires à mon application Angular 2. Dans un de mes composants, il y a un bouton avec (click)
gestionnaire. Lorsque l'utilisateur clique sur le bouton, une fonction est appelée qui est définie dans le fichier de classe .ts
. Cette fonction imprime un message dans la console.fenêtre de journal indiquant que le bouton a été pressé. Mon code de test actuel teste pour l'impression du message console.log
:
describe('Component: ComponentToBeTested', () => {
var component: ComponentToBeTested;
beforeEach(() => {
component = new ComponentToBeTested();
spyOn(console, 'log');
});
it('should call onEditButtonClick() and print console.log', () => {
component.onEditButtonClick();
expect(console.log).toHaveBeenCalledWith('Edit button has been clicked!);
});
});
Cependant, cela ne teste que la classe controller, pas le HTML. Je ne veux pas seulement tester que le la journalisation se produit lorsque onEditButtonClick
est appelée; je veux également tester que onEditButtonClick
est appelé lorsque l'utilisateur clique sur le bouton d'édition défini dans le fichier HTML du composant. Comment puis-je le faire?
4 réponses
Mon objectif est de vérifier si le 'onEditButtonClick' est invoqué lorsque l'utilisateur clique sur le bouton Modifier et ne vérifie pas seulement la console.journal imprimé.
Vous devrez d'abord configurer le test en utilisant L'angulaire TestBed
. De cette façon, vous pouvez réellement saisir le bouton et cliquez dessus. Ce que vous allez faire est de configurer un module, tout comme vous le feriez pour un @NgModule
, seulement pour l'environnement de test
import { TestBed, async, ComponentFixture } from '@angular/core/testing';
describe('', () => {
let fixture: ComponentFixture<TestComponent>;
let component: TestComponent;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ ],
declarations: [ TestComponent ],
providers[ ]
}).compileComponents().then(() => {
fixture = TestBed.createComponent(TestComponent);
component = fixture.componentInstance;
});
}));
});
Ensuite, vous devez espionner la méthode onEditButtonClick
, Cliquez sur le bouton, et vérifier que la méthode a été appelée
it('should', async(() => {
spyOn(component, 'onEditButtonClick');
let button = fixture.debugElement.nativeElement.querySelector('button');
button.click();
fixture.whenStable().then(() => {
expect(component.onEditButtonClick).toHaveBeenCalled();
})
}));
Ici, nous devons exécuter un test async
car le clic sur le bouton contient une gestion asynchrone des événements, et nous devons attendre que l'événement soit traité en appelant fixture.whenStable
Voir Aussi:
Les événements peuvent être testés en utilisant async
/fakeAsync
fonctions fournies par '@angular/core/testing'
, puisque tout événement dans le navigateur est asynchrone et poussé vers la boucle/file d'attente d'événements.
Voici un exemple très basique pour tester l'événement click en utilisant fakeAsync
.
La fonction fakeAsync
permet un style de codage linéaire en exécutant le corps de test dans une zone de test fakeAsync
spéciale.
Ici, je teste une méthode appelée par l'événement click.
it('should', fakeAsync( () => {
fixture.detectChanges();
spyOn(componentInstance, 'method name'); //method attached to the click.
let btn = fixture.debugElement.query(By.css('button'));
btn.triggerEventHandler('click', null);
tick(); // simulates the passage of time until all pending asynchronous activities finish
fixture.detectChanges();
expect(componentInstance.methodName).toHaveBeenCalled();
}));
Ci-Dessous est ce que Angulaire docs {[22] } doivent dire:
Le principal avantage de fakeAsync sur async est que le test semble être synchrone. Il n'y a pas de
then(...)
pour perturber le flux visible de contrôle. Le retour de la promessefixture.whenStable
a disparu, remplacé partick()
Il y a des limites . Par exemple, vous ne pouvez pas effectuer un appel XHR depuis un
fakeAsync
J'ai eu un problème similaire (explication détaillée ci-dessous), et je l'ai résolu (dans jasmine-core: 2.52
) en utilisant la fonction tick
avec la même (ou plus) quantité de millisecondes que dans l'appel d'origine setTimeout
.
Par exemple, si j'avais un setTimeout(() => {...}, 2500);
(donc il se déclenchera après 2500 ms), j'appellerais tick(2500)
, et cela résoudrait le problème.
Ce que j'avais dans mon composant, en réaction à un clic de bouton Delete:
delete() {
this.myService.delete(this.id)
.subscribe(
response => {
this.message = 'Successfully deleted! Redirecting...';
setTimeout(() => {
this.router.navigate(['/home']);
}, 2500); // I wait for 2.5 seconds before redirect
});
}
Elle est mon travailler essai:
it('should delete the entity', fakeAsync(() => {
component.id = 1; // preparations..
component.getEntity(); // this one loads up the entity to my component
tick(); // make sure that everything that is async is resolved/completed
expect(myService.getMyThing).toHaveBeenCalledWith(1);
// more expects here..
fixture.detectChanges();
tick();
fixture.detectChanges();
const deleteButton = fixture.debugElement.query(By.css('.btn-danger')).nativeElement;
deleteButton.click(); // I've clicked the button, and now the delete function is called...
tick(2501); // timeout for redirect is 2500 ms :) <-- solution
expect(myService.delete).toHaveBeenCalledWith(1);
// more expects here..
}));
P.S. une excellente explication sur fakeAsync
et les asyncs généraux dans les tests peut être trouvée ici: une vidéo sur les stratégies de test avec Angular 2-Julie Ralph, à partir de 8: 10, d'une durée de 4 minutes :)
Je suis l'aide de Angulaire 6. J'ai suivi la réponse de Mav55 et cela a fonctionné. Cependant, je voulais m'assurer que fixture.detectChanges();
était vraiment nécessaire, alors je l'ai enlevé et cela a toujours fonctionné. Ensuite, j'ai enlevé tick();
pour voir si cela a fonctionné et cela l'a fait. Enfin, j'ai retiré le test de l'emballage fakeAsync()
, et surprise, cela a fonctionné.
Donc je me suis retrouvé avec ceci:
it('should call onClick method', () => {
const onClickMock = spyOn(component, 'onClick');
fixture.debugElement.query(By.css('button')).triggerEventHandler('click', null);
expect(onClickMock).toHaveBeenCalled();
});
Et ça a très bien marché.