Angular Service Member Variable Not Accessible in Unit Tests

微笑、不失礼 提交于 2020-01-03 03:47:06

问题


I am trying to test my Angular service, and part of what the service does is load a JSON file that is used as configuration for the service. I have confirmed in the tests (through console.log) that the way I am mocking the HTTP.get call to get the configuration is working and is returning the mocked config object:

// 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();

When I console.log in the loadInternalAuthModuleConfiguration function, I see the object and information from the req.flush shown above. In the load function, it takes that configuration object and sets its value to a private variable in the service:

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);
        });
}

Again, console.log shows that in the .then method above that the configData comes back properly and that it is set to be the this.internalConfigData. My problem comes in the next step.

I want to check that I can access a value from that configData object after it's been set. (Remember that I ran the load function in the beforeEach.) I have a function in the service, getInternalConfig and getInternalConfigValueByKey that will either return the entire config object or a value for the specified key. When I run this in a test, I get undefined for the internalConfigData object and for the value of the passed in key.

it('should be using testing-redirect as the redirectStorage', () => {
    const configObj = authenticationService.getInternalConfig();
    const redirectStorage = authenticationService.getInternalConfigValueByKey('redirectStorage');
    expect(redirectStorage).toBe('testing-redirect');
});

That test should pass. If I console.log the internalConfigData object in the load function I can see the object I've given it. I'm not sure why it seems that this.internalConfigData is losing its data somewhere between beforeEach and when my test runs.

What am I missing here to make sure that this test runs correctly and passes?

Edit

Here is the TestBed.configureTestingModule for reference as well:

TestBed.configureTestingModule({
    imports: [HttpClientTestingModule],
    providers: [
        AuthenticationService,
        { provide: AuthenticationConfig, useValue: mockAuthConfig },
        { provide: OidcConfigService, useValue: mockOidcConfigService },
        { provide: OidcSecurityService, useValue: mockOidcSecurityService },
        { provide: localStorage, useValue: mockLocalStorage },
    ],
});

Edit 2

Here's the entire beforeEach and the related test:

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:


The problem here is that you transform http Observable to Promise and your test becomes asynchronous. It means that by the time the code reaches it statement your service doesn't have data resolved yet.

If you used Observable it would passed:

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);
    });
}

If you still want to convert observable to promise you have to wait all microtasks to be executed:

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


来源:https://stackoverflow.com/questions/55776942/angular-service-member-variable-not-accessible-in-unit-tests

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!