验证rxjs订阅中的方法调用

时间:2019-10-09 16:46:22

标签: javascript rxjs jasmine karma-runner

我有一个我要为其创建单元测试的应用程序。有一种特殊的方法,它通过可观察的管道和承诺的复杂链来创建一个可观察的对象,然后订阅所创建的可观察对象。我需要验证在订阅内部进行的方法调用。

结构是这样的:

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 */ }
    );
}

到目前为止,我的测试如下:

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();
});

当然,该测试在最后的断言中失败,因为methodToBeVerifiedWasCalled实际上是在订阅块内部调用的,并且该测试只是在同步地运行。 (在这里,classUnderTest是一个间谍对象,其中有methodToBeVerifiedWasCalled被监视。)

由于该方法为可观察对象分配了一个值,因此我不能仅在异步测试中订阅classUndertest.obs$并验证返回的值(无论如何实际上并不能帮助解决问题。)如何验证subscribe块内部的方法是否被调用?

我不能更改源代码,只能更改测试。

1 个答案:

答案 0 :(得分:1)

您需要使测试异步,然后在methodToTest()之后等待。如果methodToTest()没有返回您可以等待的承诺或可观察的结果,那么您将被迫等待一段时间。像这样:

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();
});