如何测试对 Observable 的订阅——我什么时候需要 done() 以及什么时候我必须提供第二个参数来订阅?
How to test subscription to an Observable - when do I need done() and when must I provide the second parameter to subscribe?
我正在查看 Angular 指南的 Testing Services 部分,我对该页面上一些明显的不一致之处感到困惑。
在顶部,在第一个示例中,它有一个这样的示例测试:
it('#getObservableValue should return value from observable',
(done: DoneFn) => {
service.getObservableValue().subscribe(value => {
expect(value).toBe('observable value');
done();
});
});
注意它在 subscribe
块的末尾调用 done()
。
稍后在同一页上有一些使用不同方法的测试。例如,页面上的最后一个示例有这样的测试:
it('should return expected heroes (HttpClient called once)', () => {
const expectedHeroes: Hero[] =
[{ id: 1, name: 'A' }, { id: 2, name: 'B' }];
httpClientSpy.get.and.returnValue(asyncData(expectedHeroes));
heroService.getHeroes().subscribe(
heroes => expect(heroes).toEqual(expectedHeroes, 'expected heroes'),
fail
);
expect(httpClientSpy.get.calls.count()).toBe(1, 'one call');
});
首先突出的是这个测试不调用done()
。
其次,它为 subscribe
调用提供了第二个参数(fail
函数)。 (我确实了解该参数的用途)。它实际上在附带的文本中指出提供第二个参数很重要:
The subscribe() method takes a success (next) and fail (error) callback. Make sure you provide both callbacks so that you capture errors. Neglecting to do so produces an asynchronous uncaught observable error that the test runner will likely attribute to a completely different test.
...这回避了问题:
- 为什么第二次测试不调用
done()
,
- 为什么第一个测试没有给
subscribe
提供第二个参数?
我想这两件事都有一个“充分的理由”,但我一点也不清楚那是什么 - 因此很难知道何时使用哪种模式。
我查看了文档,但对 asyncData
是什么感到困惑。但我能理解你的困惑,我想澄清一下我在单元测试中订阅时所做的事情。
我总是在单元测试的订阅结束时调用 done
以确保它通过我的断言。我认为 asyncData
returns of
数据,它使 asynchronous
可观察 synchronous
。你可以阅读它 here.
1.) 我认为对于第二种情况,就是这种情况,他们已经使 observable 与 of
同步并且他们知道 subscribe
块将被“同步”遍历.
2.) service.getObservableValue()
永远不会失败,因此他们不需要提供第二个参数。他们确信可观察对象将始终 运行 在订阅块中而不是在错误块中。
基本上,对于第二种情况,他们模拟了一个成功响应,如果它进入订阅的错误块,我们就会遇到问题,因为我们模拟了一个成功响应。稍后在文档中,他们模拟错误响应并在订阅的成功块上调用失败,因为它不应该是 运行.
在使用 observables 进行测试时始终保持语义正确:
it('should test the observable (success scenario)', (done: DoneFn) => {
yourObservable$().subscribe(response => {
expect(response).toBe('xyz');
done(); // finished all my assertions
}, fail); // if it goes to the error block, I have a bigger problem
});
it('should test the observable (error scenario)', (done: DoneFn) => {
yourObservable$().subscribe(response => {
fail(); // should not come here because we are mocking an error
}, err => {
expect(err).toBeTruthy();
done(); // finished all my assertions
});
});
当然,添加所有这些可能会使代码更加冗长。只要您确信测试做了它应该做的事情并且遍历了每个断言,您应该没问题。
我正在查看 Angular 指南的 Testing Services 部分,我对该页面上一些明显的不一致之处感到困惑。
在顶部,在第一个示例中,它有一个这样的示例测试:
it('#getObservableValue should return value from observable',
(done: DoneFn) => {
service.getObservableValue().subscribe(value => {
expect(value).toBe('observable value');
done();
});
});
注意它在 subscribe
块的末尾调用 done()
。
稍后在同一页上有一些使用不同方法的测试。例如,页面上的最后一个示例有这样的测试:
it('should return expected heroes (HttpClient called once)', () => {
const expectedHeroes: Hero[] =
[{ id: 1, name: 'A' }, { id: 2, name: 'B' }];
httpClientSpy.get.and.returnValue(asyncData(expectedHeroes));
heroService.getHeroes().subscribe(
heroes => expect(heroes).toEqual(expectedHeroes, 'expected heroes'),
fail
);
expect(httpClientSpy.get.calls.count()).toBe(1, 'one call');
});
首先突出的是这个测试不调用done()
。
其次,它为 subscribe
调用提供了第二个参数(fail
函数)。 (我确实了解该参数的用途)。它实际上在附带的文本中指出提供第二个参数很重要:
The subscribe() method takes a success (next) and fail (error) callback. Make sure you provide both callbacks so that you capture errors. Neglecting to do so produces an asynchronous uncaught observable error that the test runner will likely attribute to a completely different test.
...这回避了问题:
- 为什么第二次测试不调用
done()
, - 为什么第一个测试没有给
subscribe
提供第二个参数?
我想这两件事都有一个“充分的理由”,但我一点也不清楚那是什么 - 因此很难知道何时使用哪种模式。
我查看了文档,但对 asyncData
是什么感到困惑。但我能理解你的困惑,我想澄清一下我在单元测试中订阅时所做的事情。
我总是在单元测试的订阅结束时调用 done
以确保它通过我的断言。我认为 asyncData
returns of
数据,它使 asynchronous
可观察 synchronous
。你可以阅读它 here.
1.) 我认为对于第二种情况,就是这种情况,他们已经使 observable 与 of
同步并且他们知道 subscribe
块将被“同步”遍历.
2.) service.getObservableValue()
永远不会失败,因此他们不需要提供第二个参数。他们确信可观察对象将始终 运行 在订阅块中而不是在错误块中。
基本上,对于第二种情况,他们模拟了一个成功响应,如果它进入订阅的错误块,我们就会遇到问题,因为我们模拟了一个成功响应。稍后在文档中,他们模拟错误响应并在订阅的成功块上调用失败,因为它不应该是 运行.
在使用 observables 进行测试时始终保持语义正确:
it('should test the observable (success scenario)', (done: DoneFn) => {
yourObservable$().subscribe(response => {
expect(response).toBe('xyz');
done(); // finished all my assertions
}, fail); // if it goes to the error block, I have a bigger problem
});
it('should test the observable (error scenario)', (done: DoneFn) => {
yourObservable$().subscribe(response => {
fail(); // should not come here because we are mocking an error
}, err => {
expect(err).toBeTruthy();
done(); // finished all my assertions
});
});
当然,添加所有这些可能会使代码更加冗长。只要您确信测试做了它应该做的事情并且遍历了每个断言,您应该没问题。