Giter Club home page Giter Club logo

Comments (5)

datencia avatar datencia commented on June 3, 2024 4

Hi, I don't know if this is the right way, but this is how I did it.

First, I want to isolate my tests from all external dependencies that are not strictly necessary. So I will not import IonicModule, I'm going to use NO_ERRORS_SCHEMA to allow any property or any element on the template and avoid errors.

Then, I've created a simple mock for the ion-nav component.

@Component({
    selector: 'ion-nav',
    template: ''
})
class IonNavMock {
    @Input() root: any;
    @Input() swipeBackEnabled: boolean;
    setRoot = jasmine.createSpy('setRoot').and.returnValue(null);
}

It has the same selector as the real one, and it uses Jasmine to create a spy on the method we're mocking out.

This is how my TestBed looks like:

beforeEach(async(() => {
    TestBed.configureTestingModule({
        schemas: [ NO_ERRORS_SCHEMA ],
        declarations: [MyApp, IonNavMock],
        providers: [
            { provide: StatusBar, useClass: StatusBarMock },
            { provide: SplashScreen, useClass: SplashScreenMock },
            { provide: Platform, useClass: PlatformMock }
        ]
    });
}));

Next step is to wrap the component's creation into an async function
(because of the promise used in the component's constructor)

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

The last thing I did is to change the way we get a reference for the ion-nav, instead of using the class name we'll use the name used for querying, content. In the app.component.ts:

@ViewChild(Nav) nav: Nav;

becomes

@ViewChild('content') nav: Nav;

Now we are able to test using a spy the delayed setRoot method.

it('should set root to Page1', () => {
    expect(component.nav.setRoot).toHaveBeenCalledWith(Page1);
});

from ionic-unit-testing-example.

spacepope avatar spacepope commented on June 3, 2024

What i've achieved so far is injecting the NavController to the test method, create a spy and then wrap the expectiation inside a fakeAsync:

  it('should set root to Page1', inject([NavController], (nav: NavController) => {
    spyOn(nav, "setRoot");
    fakeAsync(() => {
      expect(nav.setRoot).toHaveBeenCalledWith(Page1);
      expect(component.rootPage).toBe(Page1);
    });
  }));

This test does actually pass, but the jasmine console output shows loooong error messages, complaining:

ERROR: Error{rejection: Error{ngComponent: function Page1(navCtrl) { ... }, __zone_symbol__currentTask: ZoneTask{_zone: ..., runCount: ..., _zoneDelegates: ..., _state: ..., type: ..., source: ..., data: ..., scheduleFn: ..., cancelFn: ..., callback: ..., invoke: ...}}, promise: ZoneAwarePromise{__zone_symbol__state: 0, __zone_symbol__value: Error{ngComponent: ..., __zone_symbol__currentTask: ...}}, zone: Zone{_properties: Object{isAngularZone: ...}, _parent: Zone{_properties: ..., _parent: ..., _name: ..., _zoneDelegate: ...}, _name: 'angular', _zoneDelegate: ZoneDelegate{_taskCounts: ..., zone: ..., _parentDelegate: ..., _forkZS: ..., _forkDlgt: ..., _forkCurrZone: ..., _interceptZS: ..., _interceptDlgt: ..., _interceptCurrZone: ..., _invokeZS: ..., _invokeDlgt: ..., _invokeCurrZone: ..., _handleErrorZS: ..., _handleErrorDlgt: ..., _handleErrorCurrZone: ..., _scheduleTaskZS: ..., _scheduleTaskDlgt: ..., _scheduleTaskCurrZone: ..., _invokeTaskZS: ..., _invokeTaskDlgt: ..., _invokeTaskCurrZone: ..., _cancelTaskZS: ..., _cancelTaskDlgt: ..., _cancelTaskCurrZone: ..., _hasTaskZS: ..., _hasTaskDlgt: ..., _hasTaskDlgtOwner: ..., _hasTaskCurrZone: ...}}, task: ZoneTask{_zone: Zone{_properties: ..., _parent: ..., _name: ..., _zoneDelegate: ...}, runCount: 0, _zoneDelegates: null, _state: 'notScheduled', type: 'microTask', source: 'Promise.then', data: Object{__creationTrace__: ...}, scheduleFn: undefined, cancelFn: null, callback: function () { ... }, invoke: function () { ... }}, longStack: 'Error: Uncaught (in promise): Error: No component factory found for Page1. Did you add it to @NgModule.entryComponents?
Error: No component factory found for Page1. Did you add it to @NgModule.entryComponents?
    at noComponentFactoryError (http://localhost:9876/base/karma-test-shim.js?649d1ac0157ac456d8bfbc1c867f4253eb1b78fc:3580:34)
.....

So how to solve this? How can an entry component be added to TestBed??

from ionic-unit-testing-example.

spacepope avatar spacepope commented on June 3, 2024

Thanks @datencia, thats a nice example. πŸ‘

What i also did wrong (and did not mentioned in my question) was, that i included IonicModule.forRoot() into TestBed configuration. I think that caused a lot of trouble, which i didn't understand until i tried your code - then conflicting components were reported..

I'm still interested: is there also a possibility to inject and fake NavController instead of mocking the directive?

from ionic-unit-testing-example.

datencia avatar datencia commented on June 3, 2024

Hi @spacepope sorry for the delay in the answer but I have had some days off and forgot to do it.

ion-nav is the declarative component for a NavController. I think that to mock the underlying NavController you need to import the IonicModule in order to access the real ion-nav component, then you need to find a way to mock its NavController. This is something I always avoid because the purpose of a spec is to test the component, not the services involved, and the time required to execute a simple spec can go from milliseconds to a few seconds.

However, this is how I did it:

  1. First, I've included the Page1 into the TestBed overriding the testing module. This is necessary because TestBed doesn't support entryComponents (Note I'm including the Page1 under the declarations array as well).
beforeEach(async(() => {
    TestBed.configureTestingModule({
        declarations: [
            MyApp,
            Page1,
        ],
        imports: [
            IonicModule.forRoot(MyApp),
        ],
        providers: [
            { provide: StatusBar, useClass: StatusBarMock },
            { provide: SplashScreen, useClass: SplashScreenMock },
            { provide: Platform, useClass: PlatformMock },
        ]
    })
    .overrideModule(BrowserDynamicTestingModule, {
        set: {
            entryComponents: [ Page1 ],
        },
    });
}));
  1. Second, I've create a spy on the setRoot method for the component nav instance in the second beforeEach.
    beforeEach(async(() => {
        fixture = TestBed.createComponent(MyApp);
        component = fixture.componentInstance;
        spyOn(component.nav, 'setRoot');
        // spyOn(component.nav, 'setRoot').and.callFake(() => console.log('our mocked function'));
    }));

This is necessary because the setRoot method is invoked in the constructor.

  1. Then I write the spec as follows:
it('should navigate to page1', () => {
    expect(component.nav.setRoot).toHaveBeenCalledWith(Page1);
});

The magic here is that I'm 'mocking' the setRoot method using spies.

I hope you find this useful and answer your question.

from ionic-unit-testing-example.

spacepope avatar spacepope commented on June 3, 2024

Thank you very much @datencia for your detailed explanations!

This should definitely be pinned in the docs or faq..
I am closing this now, as my questions are answered.

from ionic-unit-testing-example.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.