1
votes

We have a legacy Angular 4 project using SystemJS. We have been asked to implement unit test cases using Jasmine and Karma. I started writing a test suite for one component. It looks something like this (pseudo code below):

beforeEach(async(() => {
     TestBed.configureTestingModule(
                imports,
                providers,
                declarations etc.
            ).compileComponents();
}));

We have a lot of initialization code in an APP_INITIALIZER, that sets up various pieces of state in the application (such as Reference data, configuration, user information). For the test cases to run smoothly, we need the Initialization code to run. Any idea how this can be achieved?

1
I would mock all the needed state with reasonable test values as an exportable class, save it as a file in a shared directory, then import that into all your relevant test .spec files.dmcgrandle
Do you mean as a typescript file with hardcoded values, something like constants?Vikas
Yes. For example I have a project where I inject the values from initialization into various components using DI from a class called AppConfig. For use in my specs I created AppConfigMock which contains some reasonable values for various parts and in the providers array of the testbed simply { provide: AppConfig, useClass: AppConfigMock }.dmcgrandle
The ones I have are actually services (singletons), with shared state. For e.g. a UserService which holds details of the currently logged in user (UserID, his Role,etc). If, I were to use the above mechanism, I would need to inject the data into the service; that is not how it is currently structured. The services now have a .load() method that loads data from the web service and sets its internal state. That state is used across the application. Is there a way I could get access to the singleton service instance for the service. Then I could set some reasonable values through a public method.Vikas
Post code for the Singleton service and an example of its use you are trying to test.dmcgrandle

1 Answers

1
votes

If you're trying to actually run your App Initializer, modify your test's beforeEach method to setup the testbed to make it similar to your NgModule:

TestBed.configureTestingModule({
    declarations: [],
    providers: [
        {
            provide: APP_INITIALIZER,
            useFactory: <factory function here>,
            multi: true,
            deps: [
                <any dependencies of your initializer here>
            ]
        }
    ],
    imports: [
        <if you use HTTP, add 'HttpClientMOdule' here>
    ]
});

Note that many will recommend using a mock or spy instead of the actual factory that you use in production. That choice is up to you and depends on your specific needs.

After you call configureTestingModule, you will also need to do this (see https://github.com/angular/angular/issues/24218) and mark your beforeEach as async:

await TestBed.inject(ApplicationInitStatus).donePromise;

Hope this helps!