Угловой компонент тестирования, который изменяется в зависимости от наблюдаемого сервиса
Я пытаюсь проверить 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 вещи:
this.loginError
установлен на неопределенныйthis.loadingService.present('Logging In...')
называетсяthis.authService.login(this.form.value)
называется- Если
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);