angular - 仅在运行 Angular Jasmine 测试时未定义对 observable 的订阅,但在运行应用程序本身时定义
问题描述
我为此功能编写了一个单元测试:
getCarsAndSetup(){
this.getCars();
this.getFactoryInfo();
}
这是 getCars() 函数:
getCars() {
const subscription = this.carDetailsService.getAll().subscribe((carDetails) => {
this.carInfoService.setCars(carDetails);
subscription.unsubscribe(); <-------------- Here the
subscription is undefined
when running the test,
however when running
the app, the subscription
is defined and
everything is fine
});
}
这是单元测试:
fdescribe('getCarsAndSetup', () => {
it('should get cars and set them up', () => {
component.getFactoriesAndUnsubscribe();
spyOn(component, "getCars");
spyOn(component, "getFactoryInfo");
expect(component.getCars).toHaveBeenCalled();
expect(component.getFactoryInfo).toHaveBeenCalled();
});
});
我正在为 carDetailsService 使用模拟。这是 carDetailsService 模拟中的 getAll() 方法:
getAll(): Observable<CarModel[]> {
return Observable.create((observer:any) => {
observer.next([]);
});
}
这与 REAL carDetailsService 中的方法相同:
getAll(): Observable<CarModel[]> {
return this.http.get<CarModel[]>(this.carUrl);
}
问题是,当我运行应用程序本身时,定义了 getCars() 方法中的订阅,我可以取消订阅等等,一切都很好。
但是,当我运行测试时,此测试失败,因为由于某种原因,当我尝试取消订阅时,getCars() 函数中未定义订阅。
仅在运行测试时未定义订阅的原因可能是什么?这可能与我嘲笑 carDetailsService 的 getAll() 函数的方式有关吗?
解决方案
这里的问题是您依赖源 Observable 的同步/异步行为。
在您的真实应用程序中,您this.carDetailsService.getAll()
是一个真正的远程调用(异步),因此它的订阅被分配给subscription
并且一切正常。但是,在您的测试中,可能会模拟相同的调用,因此是同步的,因此在您要调用subscription.unsubscribe()
它时仍然是undefined
(该subscribe
方法仍在执行并且尚未返回任何订阅)。
您可以做的最简单的事情是传递一个箭头函数来subscribe
使用function
关键字。RxJS 将this
订阅者处理程序内部绑定到其内部订阅对象(我知道这是一个有点棘手的方法,但它打算以这种方式使用)。
const that = this;
this.carDetailsService.getAll().subscribe(function(carDetails) { // note the `function` keyword
that.carInfoService.setCars(carDetails);
this.unsubscribe();
});
另一种方法可能是使用takeUntil
Subject 并在您的subscribe
.
这种行为将来可能会改变:https ://github.com/ReactiveX/rxjs/issues/3983
不同用例中的相同问题:RxJs: Calculating observable array length in component
推荐阅读
- vue.js - 如何使用 Composition API 访问 Vuex 地图助手
- git - 如何根据 repo 状态打开和关闭 git 子模块
- gpu - 未检测到 nvidia gpu,甚至尝试从 BIOS 启用/禁用 igpu 和主显示器
- javascript - 从另一个文件中的另一个组件更改一个组件的状态
- c# - 是否可以在不离开应用程序的情况下使用按钮组织智能手机上所有通知的显示?
- python - (Python3) FLASK ADMIN:可以充分利用表格查看数据
- c - C 代码会自动查找参数 argc 的数量吗?
- javascript - 如何验证 UTC 格式日期
- java - 如何使用流将地图值聚合到集合中
- excel - 用基于另一个单元格值的非空白单元格填充空白单元格值