Переменная члена угловой службы недоступна в модульных тестах - PullRequest
1 голос
/ 20 апреля 2019

Я пытаюсь протестировать свою службу Angular, и часть того, что делает служба, - это загрузить файл JSON, который используется в качестве конфигурации для службы. В тестах я подтвердил (через console.log), что способ, которым я издеваюсь над вызовом 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.log в функции 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.log показывает, что в вышеприведенном методе .then что configData возвращается правильно и что оно установлено на this.internalConfigData. Моя проблема приходит на следующем этапе.

Я хочу проверить, могу ли я получить доступ к значению этого объекта configData после его установки. (Помните, что я запустил функцию load в beforeEach.) У меня есть функция в службе getInternalConfig и getInternalConfigValueByKey, которая либо возвращает весь объект конфигурации, либо значение для указанного ключа. Когда я запускаю это в тесте, я получаю неопределенное значение для объекта 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.log объект internalConfigData в функции 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 },
    ],
});

Редактировать 2

Вот весь 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');
});

1 Ответ

4 голосов
/ 21 апреля 2019

Проблема здесь в том, что вы преобразуете 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(() => {
  ...
}));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...