Угловой компонент тестирования, который изменяется в зависимости от наблюдаемого сервиса

Я пытаюсь проверить login() метод компонента в Angular 4, который опирается на наблюдаемый authService который возвращает либо успех, либо ошибку:

Тестируемый код:

login() {
    this.loginError = undefined;
    this.loadingService.present('Logging In...');

    this.authService
      .login(this.form.value)
      .subscribe(
        (currentUser: any) => {
          this.loadingService.dismiss();
          this.navCtrl
            .setRoot('TabsPage')
        },
        (error: any) => {
          this.loadingService.dismiss();
          this.loginError = error._body;
          console.error('LoginPage :: Login Error:', error);
        }
      );
}

Я знаю, что для успешного юнит-тестирования этого метода мне нужно его изолировать. Я создал заглушку для authServiceи ввел его в TestBed:

Заглушка аутентификации

export class AuthenticationServiceStub {

  login() {};

  getCurrentUserFromStorage() {};

  logout() {};

};

Конфигурация тестового стенда:

TestBed.configureTestingModule({
      declarations: [
        ...components,
      ],
      providers: [
        NavController,
        LoadingService,
        FormBuilder,
        { provide: AuthService, useClass: AuthenticationServiceStub }
      ],
      imports: [
        IonicModule.forRoot(LoginPage),
        PierDataServicesModule.forRoot(),
      ],
    });

Насколько я понимаю, что юнит тест login() Я должен проверить эти 4 вещи:

  1. this.loginError установлен на неопределенный
  2. this.loadingService.present('Logging In...') называется
  3. this.authService.login(this.form.value) называется
  4. Если this.authService.login(this.form.value) возвращает успех, при этом оба метода в функциональном блоке успеха вызываются, а в случае ошибки - методы в блоке ошибки.

Вот как выглядит моя спецификация (мне удалось протестировать 1, 2, 3):

тесты:

describe('Login()', () => {

  let fixture:      ComponentFixture<LoginPage>;
  let instance: any = null;
  let authService: any;

  beforeEach(async(() => TestUtils.beforeEachCompiler([LoginPage]).then(compiled => {

    fixture = compiled.fixture;
    instance = compiled.instance;

    spyOn(instance, 'login').and.callThrough();
    spyOn(instance.loadingService, 'present');
    spyOn(instance.authService, 'login').and.returnValue({ subscribe: () => {} });

    instance.login();

  })));

  it("should be callable", async(() => {
    expect(instance.login).toHaveBeenCalled();
  }));

  it("should call loadingService.present ", async(() => {
    expect(instance.loadingService.present).toHaveBeenCalled();
  }));

  it("should call authService.login", async() => {
    expect(instance.authService.login).toHaveBeenCalled();
  });


});

Я не могу понять, как проверить номер 4. Как я могу воспроизвести вызываемый Observable с успехом или ошибкой и проверить, работают ли функции в этих телах функций?

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

Должен ли я использовать реальный authService метод и построить макет бэк-энда? это то, что работает? если да, то как?

Должен ли я использовать карму для spyOn методы и изменить их, чтобы делать то, что я хочу? Я предполагал, что смогу сделать что-то вроде этого: spyOn(instance.authService, 'login').and.callThrough().and.throwError() но не имели успеха с таким подходом.

1 ответ

Решение

Реальный сервис не должен участвовать в модульном тестировании, потому что это приведет к появлению дополнительных движущихся частей.

Чистый способ проверить обещания и наблюдаемые - это предоставить реальное, а не объект, имитирующий их:

spyOn(instance.authService, 'login').and.returnValue(Observable.of(null));
...
expect(instance.loadingService.dismiss).toHaveBeenCalled();
expect(instance.navCtrl.setRoot).toHaveBeenCalledWith('TabsPage');
expect(instance.lognError).toBe(...);
...
const _body = {};
spyOn(instance.authService, 'login').and.returnValue(Observable.throw({ _body }));
...
expect(instance.loadingService.dismiss).toHaveBeenCalled();
expect(instance.navCtrl.setRoot).not.toHaveBeenCalled();
expect(instance.lognError).toBe(_body);
Другие вопросы по тегам