Как проверить побочные эффекты подписки в угловом компоненте - PullRequest
2 голосов
/ 02 ноября 2019

У меня есть угловой компонент, MyComponent , который вызывает метод, возвращающий Observable, и подписывается на него, что-то вроде

export class MyComponent {
  someProperty;
  constructor(service: MyService) {}
  someButtonClicked() {
    this.service.doStuffAsync().subscribe(
      resp => this.someProperty = resp;
    );
  }
}

@Injectable()
export MyService {
  doStuffAsync() {
    // returns an Observable which notifies asychronously, e.g. like HttoClient.get(url)
  }
}

Я хочу протестировать метод someButtonClicked() ипоэтому я создаю класс MyServiceMock , который добавляю в тесте

export class MyServiceMock {
  doStuffAsync() {
    return of({// some object}).pipe(observeOn(asyncScheduler));
  }
}

По какой-то причине я хочу, чтобы doStuffAsync() из MyServiceMock был асинхронным, поэтому observeOn(asyncScheduler)

На данный момент, хотя я не знаю, как проверить someButtonClicked(). Я пробовал разные стратегии, например, следующую

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      providers: [
        { provide: MyService, useClass: MyServiceMock },
      ]
    }).compileComponents();
  }));

  let fixture: ComponentFixture<MyComponent>;
  let component: MyComponent;

  beforeEach(() => {
    fixture = TestBed.createComponent(MyComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });
  it('test someButtonClicked', async(() => {
    component.someButtonClicked();
    fixture.whenStable().then(() => {
      expect(component.someProperty).toBeDefined();
    });
  }));

, но это не помогло, поскольку doStuffAsync() из MyServiceMock асинхронный.

Так что мой вопрос - какая стратегия самая лучшаяпроверить побочные эффекты (т. е. someProperty устанавливаются правильно), вызванные методом, который подписывается на асинхронную наблюдаемую.

Ответы [ 2 ]

1 голос
/ 02 ноября 2019
 // All code should be synchronous within this it block
    it('test someButtonClicked', async(() => {
      // sync - button clicks are synchronous
      component.someButtonClicked();

      // but after btn clicks, we need to let some async code to finish
      // like rxjs subscriptions etc.
      await fixture.whenStable();
      // and then continue

      expect(component.someProperty).toBeDefined();
    }));

Не используйте тогда fixture.whenStable().then()

0 голосов
/ 03 ноября 2019

Я использую вспомогательную функцию с именем emitted

export const emitted = obs$ => new Promise(function(resolve, reject) {
  const emitted$ = new Subject();
  obs$.pipe(takeUntil(emitted$)).subscribe(
    _ => {
      emitted$.next();
      emitted$.complete();
      resolve(true);
    },
    _ => {
      emitted$.next();
      emitted$.complete();
      reject('An error occurred');
    }
  )
});

Если ваши фиктивные сервисы совместно используют один и тот же экземпляр наблюдаемого с компонентом

export class MyServiceMock {
  private obs$ = of({// some object}).pipe(observeOn(asyncScheduler));

  doStuffAsync() {
    return obs$;
  }
}

внутри вашей спецификации, вы можете ожидать егоиспустили

it('test someButtonClicked', async(async () => {
  component.someButtonClicked();
  const service = TestBed.get(MyService);
  await emitted(service.doStuffAsync());
  expect(component.someProperty).toBeDefined();
}));

Видя, что вы подписались на тот же экземпляр наблюдаемой, что и компонент, вы знаете, компонент выполнял его функцию наблюдателя так же, как вы подписались после того, как компонент сделал.

...