[英]Subscription to an observable is undefined only when running Angular Jasmine test, but is defined when running the app itself
I have written a unit test for this function:我为这个函数写了一个单元测试:
getCarsAndSetup(){
this.getCars();
this.getFactoryInfo();
}
This is the getCars() function:这是 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
});
}
This is the unit test:这是单元测试:
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();
});
});
I am using a mock for carDetailsService.我正在使用 carDetailsService 的模拟。 This is the getAll() method in the carDetailsService mock:这是 carDetailsService 模拟中的 getAll() 方法:
getAll(): Observable<CarModel[]> {
return Observable.create((observer:any) => {
observer.next([]);
});
}
And this is the same method in the REAL carDetailsService:这与 REAL carDetailsService 中的方法相同:
getAll(): Observable<CarModel[]> {
return this.http.get<CarModel[]>(this.carUrl);
}
The problem is that when I run the application itself, the subscription in the getCars() method is defined, I can unsubscribe from it etc. and everything is fine.问题是,当我运行应用程序本身时,getCars() 方法中的订阅已定义,我可以取消订阅等等,一切都很好。
However when I run the tests, this test fails, because for some reason the subscription is undefined in the getCars() function when I try to unsubscribe from it.但是,当我运行测试时,此测试失败,因为由于某种原因,当我尝试取消订阅时,getCars() 函数中未定义订阅。
What could be the reason that the subscription is undefined only when running the test?仅在运行测试时订阅未定义的原因可能是什么? Could it have something to do with the way I've mocked the getAll() function of carDetailsService?这可能与我嘲笑 carDetailsService 的 getAll() 函数的方式有关吗?
The problem here is that you rely on synchronous/asynchronous behaviour of your source Observable.这里的问题是您依赖源 Observable 的同步/异步行为。
In your real app your this.carDetailsService.getAll()
is a real remote call (asynchronous) so its subscription is assigned to subscription
and everything works.在您的真实应用程序中,您的this.carDetailsService.getAll()
是一个真正的远程调用(异步),因此它的订阅被分配给subscription
并且一切正常。 In your tests however the same call is probably mocked and therefore synchronous so by the time you want to call subscription.unsubscribe()
it's still undefined
(the subscribe
method is still executing and no subscription has been returned yet).然而,在您的测试中,相同的调用可能被模拟,因此是同步的,因此当您要调用subscription.unsubscribe()
它仍然undefined
( subscribe
方法仍在执行并且尚未返回订阅)。
The easiest thing you can do is instead passing an arrow function to subscribe
use function
keyword.您可以做的最简单的事情是传递一个箭头函数来subscribe
use function
关键字。 RxJS binds this
inside subscriber handlers to its internal Subscription object (I know it's a bit tricky approach but it's intended to be used this way). RxJS结合this
里面用户处理其内部认购对象(我知道这是一个有点棘手的方法,但它打算使用这种方式)。
const that = this;
this.carDetailsService.getAll().subscribe(function(carDetails) { // note the `function` keyword
that.carInfoService.setCars(carDetails);
this.unsubscribe();
});
Another method could be using takeUntil
with a Subject and completing it inside your subscribe
.另一种方法可能是将takeUntil
与 Subject 一起使用并在您的subscribe
完成它。
This behavior might change in the future: https://github.com/ReactiveX/rxjs/issues/3983这种行为将来可能会改变: https : //github.com/ReactiveX/rxjs/issues/3983
The same problem in a different use-case: RxJs: Calculating observable array length in component不同用例中的相同问题: RxJs: Calculating observable array length in component
While martin's answer did get rid of the error, it helped me discover the actual problem here, which is ridiculously silly.虽然马丁的回答确实消除了错误,但它帮助我发现了这里的实际问题,这太愚蠢了。 I had set up the spies AFTER the actual function call:我在实际的函数调用之后设置了间谍:
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();
});
});
When the spies had to be defined BEFORE the actual function call:当必须在实际函数调用之前定义间谍时:
fdescribe('getCarsAndSetup', () => {
it('should get cars and set them up', () => {
spyOn(component, "getCars");
spyOn(component, "getFactoryInfo");
component.getFactoriesAndUnsubscribe();
expect(component.getCars).toHaveBeenCalled();
expect(component.getFactoryInfo).toHaveBeenCalled();
});
});
I feel bad that martin spent so much time on this answer and reading the long description I posted, and it turns out the whole problem was just a small oversight.我很难过马丁花了这么多时间在这个答案上并阅读了我发布的长篇描述,结果发现整个问题只是一个小小的疏忽。 But it is what it is.但是它就是这样啊。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.