Because we removed environment files and we load the config data before bootstraping the app module, we have a problem with the injected fields (are undefined in test files)
main.ts
fetch('./assets/config/config.json')
.then(response => response.json())
.then((config: AppConfig) => {
platformBrowserDynamic([{
provide: APP_CONFIG,
useValue: config
}]).bootstrapModule(AppModule)
.catch(err => console.error(err));
});
app.module.ts
...
export class AppModule { }
export const APP_CONFIG = new InjectionToken<AppConfig>('ENV_DATA');
and we use the APP_CONFIG
object injected in our service as:
constructor(@Inject(APP_CONFIG) private config: AppConfig) {}
The problem is in app.component.spec.ts. If we have a ItemsService
(with a dependency for APP_CONFIG) used in app
, the test will fail with a strange error message:
Uncaught ReferenceError: Cannot access 'AppComponent' before initialization
ReferenceError: Cannot access 'AppComponent' before initialization
spec file
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
RouterTestingModule
],
declarations: [
AppComponent
],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
});
anyway, if I inject {provide: APP_CONFIG, useValue: {}}
in configureTestingModule
providers array, the error still happening.
Tried to instantiate the component and inject the service in it case, but still not working:
it('should check service injection', inject([ItemsService], (itemsService: ItemsService) => {
let fix = TestBed.createComponent(AppComponent);
let comp = fix.componentInstance;
expect(comp).toBeTruthy();
}));
Another strange behavior:
If I Inject the string token in ItemsService, as constructor(@Inject('ENV_DATA') private config: AppConfig) {}
the tests will work (also, require to inject 'ENV_DATA' in configureTestingModule
!!!! But, the build will fail, because ENV_DATA
(in main.ts) is not injected in application.
Has anyone experienced something similar? thx
CodePudding user response:
If AppComponent
depends on ItemsService
, in the unit test I would mock ItemsService
.
// !! Declare mock
let mockItemsService: jasmine.SpyObj<ItemsService>;
beforeEach(async () => {
// !! Create mock
mockItemsService = jasmine.createSpyObJ<ItemsService>('ItemsService', {}, {});
await TestBed.configureTestingModule({
imports: [
RouterTestingModule
],
declarations: [
AppComponent
],
providers: [
// !! Provide the mock for the real.
{ provide: ItemsService, useValue: mockItemsService }
]
}).compileComponents();
});
Here is how to mock components depending on services: https://testing-angular.com/testing-components-depending-on-services/#testing-components-depending-on-services
CodePudding user response:
I found something working based on changing the application targeting from es2017
to es5
and adding the line "emitDecoratorMetadata": false
.
but it looks like a tradeoff, not a solution