【问题标题】:Angular Service Member Variable Not Accessible in Unit TestsAngular 服务成员变量在单元测试中不可访问
【发布时间】:2019-04-21 13:02:28
【问题描述】:

我正在尝试测试我的 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();

当我在loadInternalAuthModuleConfiguration 函数中console.log 时,我看到上面显示的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 对象后访问它。 (请记住,我在beforeEach 中运行了load 函数。)我在服务中有一个函数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');
});

该测试应该通过。如果我在load 函数中console.log internalConfigData 对象,我可以看到我给它的对象。我不确定为什么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');
});

【问题讨论】:

  • 也许您有多个服务实例。其中只有一个获得模拟值。你的 providers 数组是什么样的?
  • @Isaac 我在问题的底部添加了 TestBed.configureTestingModule 和 providers 数组。
  • 首先我使用了测试平台,建议不要使用它。但既然你想问,我的下一个问题是你如何处理服务来向它提问?
  • @JosephEames 我添加了更多代码来说明这一点。我会说,除了 CLI 生成的测试文件中的内容之外,我没有出于任何特定原因使用 TestBed。如果有更好的方法,我愿意接受。
  • 这是 CLI 的严重问题之一。测试文件默认使用测试平台。那是愚蠢的。所有 Angular 代码(模板之外)都是简单的 JS 类。你可以使用基本的 jasmine/karma 来测试它们。所以测试台的唯一需要是如果你想用一个组件来测试一个模板。

标签: angular unit-testing jasmine


【解决方案1】:

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

【讨论】:

  • 在这种情况下,它必须转换为一个承诺,所以在 beforeEach 周围使用 async 效果很好。谢谢!
  • 只是好奇,为什么它需要成为一个承诺?
猜你喜欢
  • 2022-10-15
  • 1970-01-01
  • 2019-05-06
  • 1970-01-01
  • 2019-12-19
  • 1970-01-01
  • 1970-01-01
  • 2017-06-03
  • 1970-01-01
相关资源
最近更新 更多