Home > Back-end >  Inject environment data loaded at runtime, in test files
Inject environment data loaded at runtime, in test files

Time:01-30

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

  • Related