Проверить вызов метода внутри подписки rxjs

У меня есть приложение angular, для которого я пытаюсь создать модульный тест. Существует особый метод, который создает наблюдаемое через сложную цепочку наблюдаемых каналов и обещаний, а затем подписывается на созданный наблюдаемый объект. Мне нужно убедиться, что вызов метода выполняется внутри подписки.

Структура примерно такая:

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$ в асинхронном тесте и проверить возвращаемое значение (что в любом случае не помогает решить проблему). Как я могу проверить, что метод внутри блока подписки называется?

Я не могу изменить исходный код, только тесты.


person Roddy of the Frozen Peas    schedule 09.10.2019    source источник


Ответы (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();
});
person Brandon    schedule 09.10.2019