I have an angular app which I'm trying to create a unit test for. There is a particular method which creates an observable through a complex chain of observable pipes and promises, and then subsequently subscribes to that created observable. I need to verify a method call is made inside of the subscription.
The structure is something like this:
private obs$: Observable<T>;
private subscription: Subscription;
methodToTest(): void {
this.obs$ = from( ... ).then( /* chain of piped observables */ );
this.subscription = this.obs$
.subscribe(
(value: T) => {
// bunch of logic
methodToBeVerifiedWasCalled();
},
(error) => { /* error handling */ }
);
}
My test so far looks like this:
it('the method to be verified is successfully called', () => {
// various mocking set up
this.mockApi.methodThatReturnsAPromise.and.returnvalue(Promise.resolve());
// etc.
this.classUnderTest.methodToTest();
// assertions for the various mocks...
expect(this.mockApi.methodThatReturnsAPromise).toHaveBeenCalled();
// etc.
// assert that the target method was called:
expect(this.classUnderTest.methodToBeVerifiedWasCalled).toHaveBeenCalled();
expect(this.classUnderTest.subscription).toBeDefined();
});
Of course, this test fails at the last assertions because the methodToBeVerifiedWasCalled
is actually called inside of the subscribe block and the test is just running through it synchronously. (Here, classUnderTest
is a spy object with methodToBeVerifiedWasCalled
being spied on.)
Since the method assigns a value to the observable, I can't just subscribe to classUndertest.obs$
in an async test and verify the value returned (which doesn't really help solve the problem anyway.) How can I verify that the method inside of the subscribe block is called?
I cannot change the source code, just the tests.
You need to make your test asynchronous and then await something after methodToTest()
. If methodToTest()
does not return a promise or observable that you could await then you will be forced to just await until some time has passed. Something like:
function delay(ms) {
return new Promise(resolve => setTimeout(ms, resolve));
}
it('the method to be verified is successfully called', async (done) => {
// various mocking set up
this.mockApi.methodThatReturnsAPromise.and.returnvalue(Promise.resolve());
// etc.
const result = this.classUnderTest.methodToTest();
// if the method actually returned a promise you could: await result;
// since it does not, you can just await time:
await delay(1000); // wait 1 second
// assertions for the various mocks...
expect(this.mockApi.methodThatReturnsAPromise).toHaveBeenCalled();
// etc.
// assert that the target method was called:
expect(this.classUnderTest.methodToBeVerifiedWasCalled).toHaveBeenCalled();
expect(this.classUnderTest.subscription).toBeDefined();
// tell jasmine test is done
done();
});
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.