Component Testing Error


#1

I’m getting the following error when trying to run tests through karma:

Error: No provider for App!
at injectionError (webpack:///node_modules/@angular/core/@angular/core.es5.js:1231:21 <- test-config/karma-test-shim.js:1506:86)
at noProviderError (webpack:///node_modules/@angular/core/@angular/core.es5.js:1269:0 <- test-config/karma-test-shim.js:1544:12)
at ReflectiveInjector_._throwOrNull (webpack:///node_modules/@angular/core/@angular/core.es5.js:2770:0 …

This error happens when the following file is run:

import { AppModule } from '../../../app/ionic/ionic-app.module';
import { async, TestBed } from '@angular/core/testing';
import { App, IonicModule, PopoverController } from 'ionic-angular';

import { IonicPlayerInfoBannerComponent } from './ionic-player-info-banner';
import { PopoverControllerMock } from '../../../../test-config/mocks-ionic';

describe('IonicPlayerInfoBannerComponent Component', () => {
    let fixture;
    let component;

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            declarations: [IonicPlayerInfoBannerComponent],
            providers: [
                { provide: PopoverController, useClass: PopoverControllerMock }
            ],
            imports: [
                IonicModule.forRoot(IonicPlayerInfoBannerComponent)
            ],
        })
    }));

    beforeEach(() => {
        fixture = TestBed.createComponent(IonicPlayerInfoBannerComponent);
        component = fixture.componentInstance;
    });

    it('should be created', () => {
        expect(component instanceof IonicPlayerInfoBannerComponent).toBe(true);
    });
});

I’ve tried putting App in the providers sections of the TestBed module configuration, but I still get the same error.

I’ve also tried the following configuration with the same error (having the real root component as the root for the IonicModule import):

        TestBed.configureTestingModule({
            declarations: [MyApp, IonicPlayerInfoBannerComponent],
            providers: [
                App,
                { provide: PopoverController, useClass: PopoverControllerMock }
            ],
            imports: [
                IonicModule.forRoot(MyApp)
            ],
        })

I cannot figure out for the life of me what I’m doing incorrectly, the test file for the main MyApp component works perfectly with this same syntax. Any help would be greatly appreciated!


#2

Try changing this to simply IonicModule, no forRoot().


#3

Tried this and still getting the same error. Not sure if this would help, but here’s what the component that’s being tested looks like:

import { IonicPlayerInfoBannerPopupComponent } from '../player-info-banner-popup/ionic/ionic-player-info-banner-popup';
import { PlayerInfoBanner } from '../player-info-banner';
import { PopoverController } from 'ionic-angular';
import { Component } from '@angular/core';

@Component({
    selector: 'player-info-banner',
    templateUrl: 'ionic-player-info-banner.html'
})
export class IonicPlayerInfoBannerComponent extends PlayerInfoBanner {

    constructor(private popoverController: PopoverController) {
        super();
    }

    protected onAccountDropDownClicked(event: MouseEvent) {
        let popover = this.popoverController.create(IonicPlayerInfoBannerPopupComponent);

        //passing the click event through allows ionic to position the popover properly
        popover.present({
            ev: event
        });
    }
}

I’m just thrown off because this sort of TestBed setup is working fine for the main app component


#4

Maybe it has to do with PlayerInfoBanner. I dislike inheritance, especially in JavaScript which has no language support for it, so I avoid it and therefore don’t have much experience with testing it.


#5

It could be using this.popoverController is having an impact on your code, as well as the protected keyword. I believe ‘protected’ limits access to classes that derive from that protected class, yet you’re importing, extending, and exporting.

I get the gist of inheritance, etc., but your code is a little confusing. Maybe try

onAccountDropDownClicked(event: MouseEvent, ownerComponent: IonicPlayerInfoBannerPopoverComponent) { ownerComponent.popoverController.create(IonicPlayerInfoComponentBanner) //code
};

And try passing references as ownerComponent, just to see what happens.
Or something like that. I’m confusing typescript, JavaScript, and c# with each other in this instance.

It’s also possible that

let fixture;
let component;

Is having an impact? Maybe initialize them with this.fixture = whatever;
And solidify who owns what, when, and where.

Or, at your beforeWhen function, maybe replace the two actions with a function that references fixture and component instead of assigning to them.

Another thought is to commit to outsourcing and create an IonicInfoBannerPopoverComponent class and bring that in to your code.

Last observation is your use of super() in a constructor. Is this a good place to use it? It is calling constructors until it reaches its root, which may be spiralling out of control