How to mock NavParams in tests?

4.4k views Asked by At

This might be an Ionic 2 only question, as I don't see NavParams in the Angular 2 docs, but some concepts might translate so I tagged both.

Given that I call navparams.get('somekey') in order to listen to parameters that are passed in, it's tricky to mock the NavParams in tests.

For example, here's how I currently do it:

export class NavParamsMock {
  public get(key): any {
    return String(key) + 'Output';
  }
}

This works for really basic tests, but what if I had a component that I have to test that it gets a specific type of Object, eg a User.

Then, I can do something like

export class NavParamsMock {
  public get(key): any {
    if (key === 'user') {
       return new User({'name':'Bob'})
    }
    return String(key) + 'Output';
  }
}

But, this doesn't work if you want to use the get(user) in another test, or even another component's spec. Say you use NavParams in 2 different components, and they both expect different result when you do get(user), it becomes increasingly tricky to mock.

Has anyone found a solution to this scenario?

4

There are 4 answers

2
raj On BEST ANSWER

You can get value of your choice by implementing your own setter method.

export class NavParamsMock {
  static returnParam = null;
  public get(key): any {
    if (NavParamsMock.returnParam) {
       return NavParamsMock.returnParam
    }
    return 'default';
  }
  static setParams(value){
    NavParamsMock.returnParam = value;
  }
}

Then in each test you can access the service and set your own params object.

beforeEach(() => {
  NavParamsMock.setParams(ownParams); //set your own params here
  TestBed.configureTestingModule({
    providers: [
      {provide: NavParams, useClass: NavParamsMock},
    ]
  });
})
3
Ka Mok On

I modified @raj's answer with my own variation of this technique. @raj's only allow you to set one parameter. Mine allows for key value storage with multiple parameters.

export class NavParamsMock {
  static returnParams: any = {};

  public get(key): any {
    if (NavParamsMock.returnParams[key]) {
       return NavParamsMock.returnParams[key];
    }
    return 'No Params of ' + key + ' was supplied. Use NavParamsMock.setParams('+ key + ',value) to set it.';
  }

  static setParams(key,value){
    NavParamsMock.returnParams[key] = value;
  }
}
0
Petros Kyriakou On

Here is an example with multiple params

NavParamsMock

export class NavParamsMock {

  static returnParams: any = {}

  public get (key): any {
    if (NavParamsMock.returnParams[key]) {
      return NavParamsMock.returnParams[key]
    }
  }

  static setParams (key, value): any {
    NavParamsMock.returnParams[key] = value
  }

}

Add to TestBed providers the following

{provide: NavParams, useClass: NavParamsMock}

Unit test

it('i am a unit test', () => {
    const navParams = fixture.debugElement.injector.get(NavParams)

    navParams.get =
      jasmine
        .createSpy('get')
        .and
        .callFake((param) => {
          const params = {
            'param1': 'value',
            'param2':  'value'
          }
          return params[param]
        })


    comp.ionViewDidLoad()
  })
2
Malina Kirn On

Rather than mocking out the class, it's easiest to just create an instance of the NavParams class, then use it. NavParams makes the data property publicly assignable, so it can be modified in each test as needed.

The below example assumes your page looks something like this:

@IonicPage()
@Component({...})
export class YourPage {
  private data: string;

  constructor(navParams: NavParams) {
    this.data = navParams.get('data');
  }
}

I.e., you call navParams.get() in your page constructor, ionViewDidLoad(), ngOnInit(), or similar initializer function. In this case, to modify the NavParams data and ensure it's used properly, you need to modify your test injected navParams.data property, then regenerate your page:

import {IonicModule, NavParams} from 'ionic-angular';
import {ComponentFixture, TestBed} from '@angular/core/testing';

describe('YourPage', () => {
  let fixture: ComponentFixture<YourPage>;
  let component: YourPage;
  const data = {data: 'foo'};
  const navParams = new NavParams(data);

  function generateFixture() {
    fixture = TestBed.createComponent(YourPage);
    component = fixture.componentInstance;
    fixture.detectChanges();
  }

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [YourPage],
      imports: [
        IonicModule.forRoot(YourPage),
      ],
      providers: [
        {provide: NavParams, useValue: navParams},
      ]
    });
    generateFixture();
  });

  describe('NavParams', () => {
    it('should use injected data', () => {
      expect(component['data']).toEqual('foo');
    });
    it('should use new injected data', () => {
      const newData = {data: 'bar'};
      navParams.data = newData;
      generateFixture();
      expect(component['data']).toEqual('bar');
    });
  });
});

If your page calls navParams.get('key') everywhere instead of assigning to a private member, then simply reassigning the navParams.data property is sufficient in each test (no need to call generateFixture() each time).