Проверете извикване на метод вътре в абонамент за rxjs

Имам ъглово приложение, за което се опитвам да създам единичен тест. Има конкретен метод, който създава наблюдаема чрез сложна верига от наблюдаеми канали и обещания и след това впоследствие се абонира за тази създадена наблюдаема. Трябва да проверя, че извикването на метод е направено вътре в абонамента.

Структурата е нещо като това:

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