简体   繁体   中英

Verify a method call inside of a rxjs subscription

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM