Я пытаюсь протестировать свою службу Angular, и часть того, что делает служба, — это загрузка файла JSON, который используется в качестве конфигурации для службы. Я подтвердил в тестах (через console.info), что способ, которым я имитирую вызов HTTP.get для получения конфигурации, работает и возвращает имитированный объект конфигурации:
// mocking the loading of the configuration in the beforeEach
authenticationService.loadInternalAuthModuleConfiguration();
const req = httpTestingController.expectOne('./internal-config.json');
req.flush({
redirectStorage: 'testing-redirect',
forbiddenStorage: 'testing-forbidden',
useAuthServerInDevelopment: true,
});
httpTestingController.verify();
Когда я console.info в функции loadInternalAuthModuleConfiguration, я вижу объект и информацию из req.flush, показанных выше. В функции load он берет этот объект конфигурации и устанавливает его значение в частную переменную в службе:
loadInternalAuthModuleConfiguration() {
return this._http
.get(this.authConfig.internalAuthModuleConfigUrl)
.toPromise()
.then((configData: any) => {
this.internalConfigData = { ...configData };
this.internalConfigSubject.next(this.internalConfigData);
this.setPrivateClassVariables();
})
.catch((err: any) => {
this.internalConfigData = null;
this.internalConfigSubject.next(this.internalConfigData);
});
}
Опять же, console.info показывает, что в методе .then выше, что configData возвращается правильно и что он установлен как this.internalConfigData. Моя проблема заключается в следующем шаге.
Я хочу проверить, могу ли я получить доступ к значению этого объекта configData после его установки. (Помните, что я запускал функцию load в beforeEach.) У меня есть функция в сервисе, getInternalConfig и getInternalConfigValueByKey, которая либо возвращает весь объект конфигурации, либо значение для указанного ключа. Когда я запускаю это в тесте, я получаю undefined для объекта internalConfigData и для значения переданного ключа.
it('should be using testing-redirect as the redirectStorage', () => {
const configObj = authenticationService.getInternalConfig();
const redirectStorage = authenticationService.getInternalConfigValueByKey('redirectStorage');
expect(redirectStorage).toBe('testing-redirect');
});
Этот тест должен пройти. Если я console.infointernalConfigData объект в load функции, я могу увидеть объект, который я ему дал. Я не уверен, почему кажется, что this.internalConfigData теряет свои данные где-то между beforeEach и запуском моего теста.
Что мне здесь не хватает, чтобы убедиться, что этот тест работает правильно и проходит?
Вот TestBed.configureTestingModule для справки:
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [
AuthenticationService,
{ provide: AuthenticationConfig, useValue: mockAuthConfig },
{ provide: OidcConfigService, useValue: mockOidcConfigService },
{ provide: OidcSecurityService, useValue: mockOidcSecurityService },
{ provide: localStorage, useValue: mockLocalStorage },
],
});
Вот весь beforeEach и соответствующий тест:
beforeEach(() => {
mockOidcConfigService = jasmine.createSpyObj(['load']);
mockOidcSecurityService = jasmine.createSpyObj(['getIsAuthorized']);
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [
AuthenticationService,
{ provide: AuthenticationConfig, useValue: mockAuthConfig },
{ provide: OidcConfigService, useValue: mockOidcConfigService },
{ provide: OidcSecurityService, useValue: mockOidcSecurityService },
{ provide: localStorage, useValue: mockLocalStorage },
],
});
httpTestingController = TestBed.get(HttpTestingController);
authenticationService = TestBed.get(AuthenticationService);
store = {};
authenticationService.loadInternalAuthModuleConfiguration();
const req = httpTestingController.expectOne('./internal-config.json');
req.flush({
redirectStorage: 'testing-redirect',
forbiddenStorage: 'testing-forbidden',
useAuthServerInDevelopment: true,
});
httpTestingController.verify();
});
it('should be using testing-redirect as the redirectStorage', () => {
const configObj = authenticationService.getInternalConfig();
const redirectStorage = authenticationService.getInternalConfigValueByKey('redirectStorage');
expect(redirectStorage).toBe('testing-redirect');
});
@Isaac Исаак Я добавил TestBed.configureTestingModule с массивом провайдеров в конец вопроса.
Во-первых, я использую тестовый стенд и рекомендую не использовать его. Но раз уж вы хотите, мой следующий вопрос: как вы связываетесь со службой, чтобы задавать ей вопросы?
@JosephEames Я добавил еще немного кода, чтобы показать это. Однако я скажу, что я не использую TestBed по какой-либо конкретной причине, кроме той, что была в тестовом файле, сгенерированном CLI. Если есть лучший способ сделать это, я открыт для него.
Это одна из ужасных проблем с CLI. тестовые файлы по умолчанию используют тестовый стенд. это глупо. Весь угловой код (за исключением шаблонов) представляет собой простые классы JS. вы можете проверить их, используя базовый жасмин/карму. Таким образом, испытательный стенд нужен только в том случае, если вы хотите протестировать шаблон с компонентом.
кроме того, я даже не уверен, что тестирует этот тест. тест должен быть заявлением о функциональности части вашего приложения. Когда вы разрабатывали службу аутентификации, вы поняли, что одной из вещей, в которой она нуждалась, было «использовать тестирование-перенаправление в качестве хранилища перенаправления»? Бьюсь об заклад, это не было. Бьюсь об заклад, это было что-то вроде «он должен использовать redirectStorage, который ему дали». Я также немного смущен. проверяет ли этот тест метод getInternalConfig или метод loadInternalAuthModuleConfiguration?
Кроме того, почему у вас есть первая строка кода в блоке it? вы ничего не утверждаете по этому поводу. Это результат временной зависимости? если нет, переместите тесты на него в свой собственный it()
@JosephEames Я забыл удалить первую строку этого теста после отладки. Я удалил это из реального теста. Я согласен с тем, что я также хотел проверить, предложенное вами имя намного лучше. Наконец, я хочу протестировать оба этих метода.
Ok. это имеет больше смысла. Хорошая сделка.





Проблема здесь в том, что вы преобразуете http Observable в Promise, и ваш тест становится асинхронным. Это означает, что к тому времени, когда код достигает оператора it, данные в вашем сервисе еще не разрешены.
Если бы вы использовали Observable, он бы прошел:
loadInternalAuthModuleConfiguration() {
return this.http
.get(this.authConfig.internalAuthModuleConfigUrl)
.subscribe((configData: any) => {
this.internalConfigData = {...configData};
this.internalConfigSubject.next(this.internalConfigData);
this.setPrivateClassVariables();
}, (err: any) => {
this.internalConfigData = null;
this.internalConfigSubject.next(this.internalConfigData);
});
}
Если вы все еще хотите преобразовать наблюдаемое в обещание, вам нужно дождаться выполнения всех микрозадач:
import { TestBed, async } from '@angular/core/testing';
...
beforeEach(async(() => {
...
}));
В этом случае его нужно преобразовать в обещание, поэтому использование async вокруг beforeEach сработало отлично. Спасибо!
просто любопытно, почему это требует обещания?
Возможно, у вас есть несколько экземпляров вашего сервиса. И только один из них получает фиктивное значение. Как выглядит ваш массив провайдеров?