angular - 如何以角度对取消订阅功能进行单元测试
问题描述
我想找到一种方法来测试订阅和主题上的取消订阅函数调用。
我想出了一些可能的解决方案,但每一个都有优点和缺点。请记住,我不想为了测试目的而更改变量的访问修饰符。
- 使用反射访问组件的私有变量。
在这种情况下,我有一个存储订阅的私有类变量:
组件.ts:
private mySubscription: Subscription;
//...
ngOnInit(): void {
this.mySubscription = this.store
.select(mySelector)
.subscribe((value: any) => console.log(value));
}
ngOnDestroy(): void {
this.mySubscription.unsubscribe();
}
组件.spec.ts:
spyOn(component['mySubscription'], 'unsubscribe');
component.ngOnDestroy();
expect(component['mySubscription'].unsubscribe).toHaveBeenCalledTimes(1);
优点:
- 我可以联系到我的订阅。
- 我可以测试在正确的订阅上调用了取消订阅方法。
缺点:
- 我只能通过反射达到 mySubscription ,如果可能的话我想避免这种情况。
- 就像在选项 1 中一样,我为订阅创建了一个变量,但是我没有通过反射来访问该变量,我只是检查了 unsubscribe 方法是否被调用,而不知道源。
component.ts:与选项 1 相同
组件.spec.ts:
spyOn(Subscription.prototype, 'unsubscribe');
component.ngOnDestroy();
expect(Subscription.prototype.unsubscribe).toHaveBeenCalledTimes(1);
优点:
- 我可以测试是否调用了取消订阅方法
缺点:
- 我无法测试调用的取消订阅方法的来源。
- 我实现了一个辅助函数,它在传递的订阅参数上调用取消订阅方法。
订阅.helper.ts:
export class SubscriptionHelper {
static unsubscribeAll(...subscriptions: Subscription[]) {
subscriptions.forEach((subscription: Subscription) => {
subscription.unsubscribe();
});
}
}
component.ts:与选项 1 相同,但 ngOnDestroy 不同:
ngOnDestroy(): void {
SubscriptionHelper.unsubscribeAll(this.mySubscription);
}
组件.spec.ts:
spyOn(SubscriptionHelper, 'unsubscribeAll');
component.ngOnDestroy();
expect(SubscriptionHelper.unsubscribeAll).toHaveBeenCalledTimes(1);
优点:
- 我可以测试是否调用了辅助函数
缺点:
- 我无法测试是否在特定订阅上调用了取消订阅功能。
你们有什么建议?你如何在单元测试中测试清理?
解决方案
我遇到了完全相同的问题,这是我的解决方案:
组件.ts:
private subscription: Subscription;
//...
ngOnInit(): void {
this.subscription = this.route.paramMap.subscribe((paramMap: ParamMap) => {
// ...
});
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
组件.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();
});
这对我有用,我希望它也对你有用!
推荐阅读
- python - 如何在 python sqlite3 嵌套查询中两次传递相同的参数?
- wordpress - 如何在 Elementor Wordpress 中编辑我的简码部分的文本
- macros - Lisp 的宏系统也可以扩展它的注释语法吗?
- javascript - 我有一个 js 脚本,它应该禁用一个按钮,但它不起作用
- powerbi - 在 Power BI 中仅使用开始日期和结束日期计算每月平均值
- android - 将原始音频从 Android 流式传输到服务器再到客户端
- java - 颤动中的Firebase通知 - 编译错误
- node.js - 更新 Firestore 中的嵌套变量字段
- python - Python - 中断语句在while循环中的else语句中不起作用
- python - 将元素添加到链表python