Ionic-framework: NavParams returning error with Jasmine tests

Created on 24 Nov 2016  路  8Comments  路  Source: ionic-team/ionic-framework

Ionic version: (check one with "x")
[ ] 1.x
[X] 2.x

I'm submitting a ... (check one with "x")
[X] bug report
[ ] feature request
[ ] support request => Please do not submit support requests here, use one of these channels: https://forum.ionicframework.com/ or http://ionicworldwide.herokuapp.com/

Current behavior:

Using NavParams in a component throws an error when testing: Error: No provider for NavParams!
Expected behavior:

It would not throw the error :smile:
Steps to reproduce:
Inject navParams into component and run any old Karma test on the component.

Related code:

import {NavParams} from 'ionic-angular';
... 
constructor (public navParams: NavParams)

Other information:

Ionic info: (run ionic info from a terminal/cmd prompt and paste output below):

Cordova CLI: 6.4.0 
Ionic Framework Version: 2.0.0-rc.3
Ionic CLI Version: 2.1.12
Ionic App Lib Version: 2.1.7
Ionic App Scripts Version: 0.0.46
ios-deploy version: 1.9.0 
ios-sim version: 5.0.8 
OS: macOS Sierra
Node Version: v6.9.1
Xcode version: Xcode 8.1 Build version 8B62

Most helpful comment

Hi all, I've solved this creating a MockNavParams class in my test file:

class MockNavParams{
  data = {
  };

  get(param){
    return this.data[param];
  }
}

Then, add it into the TestBed configuration:

beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ YourModule ],
      providers: [
        ....
        {provide: NavParams, useClass: MockNavParams},
      ],
      imports: [
        ....
      ],
    })
    .compileComponents();
  }));

Now, your test will work but if your component logic expect to do something with the param received you have two options:

Case: you expect to receive an user as param:

navParams.get('user')

EASY WAY BUT NOT RECOMMENDED:

Add the object that you want to receive in the data object inside the MockNavParams class:

class MockNavParams {
  data = {
     user: {
           _id: "001", 
           name: "Mike", 
     }
  };

  get(param){
    return this.data[param];
  }
}

RECOMMENDED WAY

Keep the data object in the MockNavParams class empty.
In your class constructor, store the navParams.get('user') value in a local variable:

this.user = navParams.get('user');

Your app logic should look for that variable instead of navParams directly. So, now in your test case, you overwrite the variable:

  beforeEach(() => {
    fixture = TestBed.createComponent(MapPage);
    component = fixture.debugElement.componentInstance;
    fixture.detectChanges();
  });

it('Should have one device if is received by navparams', () => {
    component.user = {
      _id: "001", 
     name: "Mike", 
    }

    chai.expect(component.user._id).to.be.equal("001");
  });

Since NavParams is something that happens when user navigate, you can test that behaviour using Protractor or other end to end test suite. Keep in mind that Jasmine is just for Unit test.

All 8 comments

Never mind, the issue was not injecting NavParams into providers.

The error is now this, so there is still an issue, and thus, I have reopened it:

    Failed: Can't resolve all parameters for NavParams: (?).
    Error: Can't resolve all parameters for NavParams: (?).

Can you provide a repo or plunker that reproduces your issue?
http://plnkr.co/edit/GJte2b?p=preview

Hi, I've the same issue. The problem occurs when I try to test a Page that receives something by NavParams. I think is not a Ionic issue, just we don't know how to pass that parameters throught NavParams when testing:

Here is the app logic:

  1. On my AccessPage, i send some properties to the DeviceListPage throught Navigator:
this.navCtrl.setRoot(DeviceListPage, { devices: myDevices});

I receive that properties on my DeviceListPage:

  constructor(public navCtrl: NavController, public navParams: NavParams) {
    this.devices = navParams.get('devices') || [];
  }

It works fine on the emulator, but when I try to test it I don't know where I've to tell to the test file that my Page receives those params to mock it.

That's the reason why i receive 'Can't resolve all parameters for NavParams error. How i can pass to the test file something like 'mocked params'?

This is my TestBed configuration for the device-list.spec.ts:

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ DeviceListPage ],
      providers: [
        App, DomController, Form, Keyboard, MenuController, NavController, Platform, NavParams
      ],
      imports: [
        IonicModule,
        ReactiveFormsModule,
      ],
    })
    .compileComponents();
  }));

Hi all, I've solved this creating a MockNavParams class in my test file:

class MockNavParams{
  data = {
  };

  get(param){
    return this.data[param];
  }
}

Then, add it into the TestBed configuration:

beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ YourModule ],
      providers: [
        ....
        {provide: NavParams, useClass: MockNavParams},
      ],
      imports: [
        ....
      ],
    })
    .compileComponents();
  }));

Now, your test will work but if your component logic expect to do something with the param received you have two options:

Case: you expect to receive an user as param:

navParams.get('user')

EASY WAY BUT NOT RECOMMENDED:

Add the object that you want to receive in the data object inside the MockNavParams class:

class MockNavParams {
  data = {
     user: {
           _id: "001", 
           name: "Mike", 
     }
  };

  get(param){
    return this.data[param];
  }
}

RECOMMENDED WAY

Keep the data object in the MockNavParams class empty.
In your class constructor, store the navParams.get('user') value in a local variable:

this.user = navParams.get('user');

Your app logic should look for that variable instead of navParams directly. So, now in your test case, you overwrite the variable:

  beforeEach(() => {
    fixture = TestBed.createComponent(MapPage);
    component = fixture.debugElement.componentInstance;
    fixture.detectChanges();
  });

it('Should have one device if is received by navparams', () => {
    component.user = {
      _id: "001", 
     name: "Mike", 
    }

    chai.expect(component.user._id).to.be.equal("001");
  });

Since NavParams is something that happens when user navigate, you can test that behaviour using Protractor or other end to end test suite. Keep in mind that Jasmine is just for Unit test.

We can solve it by initialize it through the jasmine spy .
Below is the code which works for me.

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [],
imports: [FormsModule, HttpModule],
providers: [
{ provide: NavParams, useClass: class { NavParams = jasmine.createSpy("NavParams"); } ],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
});
});

Also we can provide custom values if we have any like below
{
provide: NavParams, useValue: {
data: {
"MYVALUE": "XYZ"
}
}
}

Works for me, thanks for your help! @snehasis

Thanks for the issue! This issue is being locked to prevent comments that are not relevant to the original issue. If this is still an issue with the latest version of Ionic, please create a new issue and ensure the template is fully filled out.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

manucorporat picture manucorporat  路  3Comments

brandyscarney picture brandyscarney  路  3Comments

BilelKrichen picture BilelKrichen  路  3Comments

daveshirman picture daveshirman  路  3Comments

alan-agius4 picture alan-agius4  路  3Comments