在单元测试中无法访问Angular Service成员变量

时间:2019-04-20 19:24:01

标签: angular unit-testing jasmine

我正在尝试测试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函数。)服务中有一个函数getInternalConfiggetInternalConfigValueByKey,它将返回整个配置对象或一个指定键的值。当我在测试中运行此命令时,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.internalConfigDatabeforeEach到我的测试运行之间丢失了数据。

我在这里缺少什么以确保此测试正确运行并通过?

编辑

这里还有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 个答案:

答案 0 :(得分:4)

这里的问题是您将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);
    });
}

如果您仍然想将observable转换为promise,则必须等待所有微任务被执行:

import { TestBed, async } from '@angular/core/testing';  
...
beforeEach(async(() => {
  ...
}));