Je voudrais trouver un moyen de tester les appels de fonction de désabonnement sur les abonnements et les sujets.
J'ai trouvé quelques solutions possibles, mais chacune a ses avantages et ses inconvénients. Veuillez garder à l'esprit que je ne souhaite pas modifier le modificateur d'accès d'une variable à des fins de test.
Dans ce cas, j'ai une variable de classe privée qui stocke un abonnement:
component.ts:
private mySubscription: Subscription;
//...
ngOnInit(): void {
this.mySubscription = this.store
.select(mySelector)
.subscribe((value: any) => console.log(value));
}
ngOnDestroy(): void {
this.mySubscription.unsubscribe();
}
component.spec.ts:
spyOn(component['mySubscription'], 'unsubscribe');
component.ngOnDestroy();
expect(component['mySubscription'].unsubscribe).toHaveBeenCalledTimes(1);
avantages:
les inconvénients:
component.ts: identique à l'option 1
component.spec.ts:
spyOn(Subscription.prototype, 'unsubscribe');
component.ngOnDestroy();
expect(Subscription.prototype.unsubscribe).toHaveBeenCalledTimes(1);
avantages:
les inconvénients:
souscription.helper.ts:
export class SubscriptionHelper {
static unsubscribeAll(...subscriptions: Subscription[]) {
subscriptions.forEach((subscription: Subscription) => {
subscription.unsubscribe();
});
}
}
component.ts: identique à l'option 1, mais ngOnDestroy est différent:
ngOnDestroy(): void {
SubscriptionHelper.unsubscribeAll(this.mySubscription);
}
component.spec.ts:
spyOn(SubscriptionHelper, 'unsubscribeAll');
component.ngOnDestroy();
expect(SubscriptionHelper.unsubscribeAll).toHaveBeenCalledTimes(1);
avantages:
les inconvénients:
Que proposez-vous les gars? Comment testez-vous le nettoyage dans le test unitaire?
J'ai eu exactement le même problème, voici ma solution:
component.ts:
private subscription: Subscription;
//...
ngOnInit(): void {
this.subscription = this.route.paramMap.subscribe((paramMap: ParamMap) => {
// ...
});
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
component.spec.ts:
let dataMock;
let storeMock: Store;
let storeStub: {
select: Function,
dispatch: Function
};
let paramMapMock: ParamMap;
let paramMapSubscription: Subscription;
let paramMapObservable: Observable<ParamMap>;
let activatedRouteMock: ActivatedRoute;
let activatedRouteStub: {
paramMap: Observable<ParamMap>;
};
beforeEach(async(() => {
dataMock = { /* some test data */ };
storeStub = {
select: (fn: Function) => of((id: string) => dataMock),
dispatch: jasmine.createSpy('dispatch')
};
paramMapMock = {
keys: [],
has: jasmine.createSpy('has'),
get: jasmine.createSpy('get'),
getAll: jasmine.createSpy('getAll')
};
paramMapSubscription = new Subscription();
paramMapObservable = new Observable<ParamMap>();
spyOn(paramMapSubscription, 'unsubscribe').and.callThrough();
spyOn(paramMapObservable, 'subscribe').and.callFake((fn: Function): Subscription => {
fn(paramMapMock);
return paramMapSubscription;
});
activatedRouteStub = {
paramMap: paramMapObservable
};
TestBed.configureTestingModule({
// ...
providers: [
{ provide: Store, useValue: storeStub },
{ provide: ActivatedRoute, useValue: activatedRouteStub }
]
})
.compileComponents();
}));
// ...
it('unsubscribes when destoryed', () => {
fixture.detectChanges();
component.ngOnDestroy();
expect(paramMapSubscription.unsubscribe).toHaveBeenCalled();
});
Cela fonctionne pour moi, j'espère que ça le sera aussi pour vous!
Cela vous semble-t-il bon?
component.mySubscription = new Subscription();
spyOn(component.mySubscription, 'unsubscribe');
component.ngOnDestroy();
expect(component.mySubscription.unsubscribe).toHaveBeenCalledTimes(1);
Je m'éloigne des COMMENTAIRES, pour gagner un peu plus d'espace, même si le mien n'est qu'un raisonnement ouvert. J'espère que c'est acceptable par SO.
Je voudrais tester que le composant appelle désabonnement à chaque abonnement
Je pense que c'est un cas intéressant de conception de test.
Nous savons tous qu'il est important de ne pas laisser les abonnements en vie lorsqu'un composant est détruit, et il vaut donc la peine de réfléchir à la manière de le tester.
En même temps, cela pourrait être considéré comme un détail d'implémentation et il n'appartient certainement pas au " accessible au public comportement "qu'un composant SW expose via ses API. Et les API publiques devraient être au centre des tests si vous ne souhaitez pas coupler étroitement les tests et le code, ce qui empêche le refactoring.
De plus, AFAIK RxJS ne fournit pas de mécanisme pour répertorier tous les abonnements actifs à un moment donné, donc la tâche de trouver un moyen de tester cela reste à nous.
Donc, si vous souhaitez atteindre votre objectif de tester que tous les abonnements ne sont pas souscrits, vous devez adopter une conception spécifique pour le permettre, par exemple l'utilisation d'un tableau où vous stockez tous vos abonnements, puis une vérification pour voir si tous ont été fermés. C'est la ligne de votre SubscriptionHelper
.
Mais même cela ne vous met pas nécessairement en lieu sûr. Vous pouvez toujours créer un nouvel abonnement et ne pas l'ajouter au tableau, peut-être simplement parce que vous oubliez de le faire dans votre code.
Ainsi, vous ne pouvez tester cela que si vous vous en tenez à votre discipline de codage , c'est-à-dire que vous ajoutez des abonnements au tableau. Mais cela revient à vous assurer de vous désinscrire de tous vos abonnements dans la méthode ngOnDestroy
. Même cela est une discipline de codage.
Alors, est-ce vraiment logique d'avoir un tel test même si l'objectif final du test est important? Cela ne devrait-il pas être quelque chose à réaliser via la révision du code?
Très curieux de lire les opinions
Concernant votre dernier point
Je ne peux pas tester que la fonction de désabonnement a été appelée sur un abonnement spécifique.
Vous pouvez le faire en fait.
// get your subscrption
const mySubscription = component['mySubscription'];
// use the reference to confirm that it was called on that specific subscription only.
expect(SubscriptionHelper.unsubscribeAll).toHaveBeenCalledWith(mySubscription);
Maintenant, en ce qui concerne les tests, il suffit de vérifier/accéder/appeler les membres publics et tester/attendre ses effets.
En utilisant le luxe de javascript, vous attendez/validez des membres privés, même si c'est très bien Je pense. Même si je le fais, je peux me tromper. Les critiques et commentaires productifs sont les bienvenus.