6
votes

I'm working on unit tests within my Angular app , i'm using TestBed approach ,

I'm testing components , so each spec file looks like this

import...
describe('AppComponent', () => {
// Importing dependecies
    beforeEach(async(() => {
        TestBed.configureTestingModule({
            imports : [RouterTestingModule , HttpModule , FormsModule ],
            declarations: [AppComponent
            ],
            providers: [AUTH_PROVIDERS ,UserService, SharedclientService, RouteNavigator, JwtHelper, ReloadTokenService, ShopService
                , EnvVarsService, ProfileService, LocalStorageService, ApiVersionInterceptor, ApiTrackingInterceptor, MonitoringService ,
                { provide: 'LOCAL_STORAGE_SERVICE_CONFIG', useValue: userConfig } , TokenUtilService , HttpInterceptorService ,
                { provide: InterceptableStoreFactory, useClass: InterceptableStoreFactoryMock },ReloadTokenEventService , InterceptableStoreFactory

            ]

        }).compileComponents();
    }));
    // detecting changes every times
    beforeEach(() => {
        fixture = TestBed.createComponent(AppComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
    });

    // Test case 0 (compilation of the component)
    it('AppComponent is well defined', () => {
        expect(component).toBeDefined();
    });

    // Test case 1
    it('test', () => {
        expect("1").toBe("1");
    });
});

This approach of testing , is causing the failure of the whole test suite , if the dependencies importing isn't well.

For example : in this test suite , it throws this error :

No provider for InterceptableStoreFactory! It seems to be strange , as i'm importing this service in my providers (last one)

This causes almost the failure of all test cases assince the verification of the fixture imports is "beforeEach" test cases

Am looking for better ideas for :

  1. the problem of "no provider for service" (which is already added to providers"

and for

  1. unit testBed better way of testing
2

2 Answers

3
votes

You provide InterceptableStoreFactory twice. Once with a mock replacement, and once the original. Try removing one of them.

It may help you to create a module for all your services and put it in the 'core' folder. (See Angular Style Guide)

This makes it easier to provide all the right services both in test and development/production without repeating yourself so much.

2
votes

1. No provider for service

Remove the trailing InterceptableStoreFactory in the providers array. As you're already injecting the mock service InterceptableStoreFactory earlier on the same line.

If that doesn't fix it please provide snippet of your mock class InterceptableStoreFactoryMock and InterceptableStoreFactory.

2. Better Test Strategy

For a better test strategy I would suggest a few things to make testing easier:

  1. Suggested here in Angular Styleguide: Collect all global services into one CoreModule. You can then easily know that all the services are getting imported and easily import them for testing. I would also further recommend maybe creating an explicit CoreTestingModule if you want to mock many of those services the same way for many components. Thus retaining the re-usability for all tests.
  2. Suggested here in Angular Styleguide: a SharedModule in which you can export components, directives and pipes that you re-use a lot across the app.
  3. Consider using very small modules. In the FAQ in one of the conferences in the latter part of 2017 or early 2018, Rob Wormald mentioned that each module in Google Angular project only has on average 1.6 components. This means that each test of a component is much less likely to include services and other imports that it doesn't actually need. Thus also requiring a lot less refactoring of tests if you change a big module used by very many components. With smaller modules your TestBed will also be a lot faster as it goes a lot faster to re-initialize the TestBed after each test case.
  4. Consider moving most logic to services and pipes and primarily shifting test focus to them. As testing services and pipes can use regular test infrastructure and thus skip the complexity of creating modules as services and pipes can be treated just like regular classes and functions. As a side-note I'd mention that this is more or less done automatically by migrating to an NGRX architecture.
  5. This issue is tracking a bigger performance improvement to the TestBed. The problem now being that you have to re-create and tear the module down between each test case. Making it a lot slower than just testing services, pipes and other pure functions and classes. As mentioned previously though, this is remedied a bit by using smaller modules. That issue also show a few hacks to use Jest or workarounds so that the modules do not need to be re-created for each Test Case and instead re-usable. The latter however changes private API's so could easily have your app broken between patch releases of Angular, which is not a great place to be at.