问题
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