MilindaD
MilindaD

Reputation: 7653

Ionic 2 ViewController unit testing

mIve got the following unit tests that tests a Component I have written in Ionic 2. The unit tests gives an error from one of the Ionic libraries, I assume that I am not mocking it out properly or as such

import { ComponentFixture, async } from '@angular/core/testing';
import { TestUtils }               from '../../test';
import {} from 'jasmine';

import { LocationSearchModal } from './LocationSearchModal';
import { LocationService } from '../../services/LocationService';
import { PouchDbService } from '../../services/common/PouchDbService';

import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { TestBed } from '@angular/core/testing';
import { App, MenuController, NavController, Platform, Config, Keyboard, Form, IonicModule, ViewController, GestureController, NavParams }  from 'ionic-angular';
import { ConfigMock } from '../../mocks';
import { TranslateModule } from 'ng2-translate';
import { LoadingController } from 'ionic-angular';

let fixture: ComponentFixture<LocationSearchModal> = null;
let instance: any = null;

describe('LocationSearchModal', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        LocationSearchModal
      ],
      providers: [
        App, Platform, Form, Keyboard, MenuController, NavController, GestureController, LocationService, LoadingController,
        { provide: ViewController, useClass: class { ViewController = jasmine.createSpy("viewController"); } },
        { provide: NavParams, useClass: class { NavParams = jasmine.createSpy("navParams"); } },
        { provide: PouchDbService, useClass: class { PouchDbService = jasmine.createSpy("pouchDbService"); } },
        {provide: Config, useClass: ConfigMock}
      ],
      imports: [
        FormsModule,
        IonicModule,
        ReactiveFormsModule,
        TranslateModule.forRoot(),
      ],
    })
    .compileComponents()
    .then(() => {
      fixture = TestBed.createComponent(LocationSearchModal);
      instance = fixture.debugElement.componentInstance;
      fixture.autoDetectChanges(true);
    });
  }));

  afterEach(() => {
    fixture.destroy();
  });

  it('loads', () => {
    expect(fixture).not.toBeNull();
    expect(instance).not.toBeNull();
  })
})

This is the relevant excerpt which uses the ViewController from the component that is being tested.

this.locationService.getLocationById(this.selectedLocation)
      .subscribe((location: any) => {
        this.viewController.dismiss(location.doc)
      });

The test fails and I get the following stack trace

  Chrome 53.0.2785 (Linux 0.0.0)
TypeError: viewCtrl._setHeader is not a function
    at new Header (webpack:///home/milinda/workspaces/eclipse/inspection/addedinspection/Inspection-Upgrade/~/ionic-angular/components/toolbar/toolbar.js:14:0 <- src/test.ts:11833:30)
    at new Wrapper_Header (/IonicModule/Header/wrapper.ngfactory.js:7:18)

This is related to the ViewController line which I have created a jasmine spy for

{ provide: ViewController, useClass: class { ViewController = jasmine.createSpy("viewController"); } },

After having a look at the code base I found the _setHeader method in here

https://github.com/driftyco/ionic/blob/6b3e2ed447340cdd35c328c96aa7cfa5f34eb214/src/navigation/view-controller.ts#L364

I also tried writing a custom provider but got the same error as well. Any idea on what is the correct method of testing the ViewController.

Additionally sometimes after resolving the ViewController issue an issue may occur from NavParams perhaps

Upvotes: 4

Views: 3500

Answers (6)

Santosh nayak
Santosh nayak

Reputation: 223

add

{ provide: ViewController, useClass: class { ViewController = jasmine.createSpy("viewController"); } },

to provides

Upvotes: 0

Christopher
Christopher

Reputation: 1029

In case someone doesn't notice the comments on James Macmillan's answer:

@eesdil wrote:

From beenotung, there is built in mocks in ionic also: import {mockApp, mockConfig, mockPlatform, mockView} from "ionic-angular/util/mock-providers"; and use it like {provide: ViewController, useValue: mockView()}, – Sep 26 '17 at 9:40


This is the solution that worked for me.

Upvotes: 1

Yuvraj Patil
Yuvraj Patil

Reputation: 8726

1. Create Jasmine spy of ViewController.

let viewCtrlSpy = jasmine.createSpyObj('ViewController', 
                          ['data', 'readReady', 'writeReady', 'dismiss', '_setHeader', '_setNavbar', '_setIONContent', '_setIONContentRef']);

2. Use viewCtrlSpy spy in providers array as follow:

providers: [
.......
          {
            provide: ViewController,
            useValue: viewCtrlSpy
          }
..........

Spying is more efficient in this case than mocking.

Upvotes: 0

Maverick09
Maverick09

Reputation: 1075

Accepted answer did not work for ionic version 3.9.2, however following fixed the issue:

export class ViewControllerMock {

  public readReady: any = {
    emit(): void {

    },
    subscribe(): any {

    }
  };

  public writeReady: any = {
    emit(): void {

    },
    subscribe(): any {

    }
  };

  public contentRef(): any {
    return new Promise(function (resolve: Function): void {
      resolve();
    });
  }

  public didEnter(): any {
    return new Promise(function (resolve: Function): void {
      resolve();
    });
  }

  public didLeave(): any {
    return new Promise(function (resolve: Function): void {
      resolve();
    });
  }

  public onDidDismiss(): any {
    return new Promise(function (resolve: Function): void {
      resolve();
    });
  }

  public onWillDismiss(): any {
    return new Promise(function (resolve: Function): void {
      resolve();
    });
  }

  public willEnter(): any {
    return new Promise(function (resolve: Function): void {
      resolve();
    });
  }

  public willLeave(): any {
    return new Promise(function (resolve: Function): void {
      resolve();
    });
  }

  public willUnload(): any {
    return new Promise(function (resolve: Function): void {
      resolve();
    });
  }

  public dismiss(): any {
    return true;
  }

  public enableBack(): any {
    return true;
  }

  public getContent(): any {
    return true;
  }

  public hasNavbar(): any {
    return true;
  }

  public index(): any {
    return true;
  }

  public isFirst(): any {
    return true;
  }

  public isLast(): any {
    return true;
  }

  public pageRef(): any {
    return true;
  }

  public setBackButtonText(): any {
    return true;
  }

  public showBackButton(): any {
    return true;
  }

  public _setHeader(): any {
    return true;
  }

  public _setIONContent(): any {
    return true;
  }

  public _setIONContentRef(): any {
    return true;
  }

  public _setNavbar(): any {
    return true;
  }

  public _setContent(): any {
    return true;
  }

  public _setContentRef(): any {
    return true;
  }

  public _setFooter(): any {
    return true;
  }

}

Ionic Info

cli packages: 

    @ionic/cli-plugin-proxy : 1.5.6
    @ionic/cli-utils        : 1.14.0
    ionic (Ionic CLI)       : 3.14.0

local packages:

    @ionic/app-scripts : 3.1.0
    Ionic Framework    : ionic-angular 3.9.2

Upvotes: 1

James Macmillan
James Macmillan

Reputation: 176

Building on Marky Sparky's answer. As of ionic 3+:

export class ViewControllerMock{
  readReady = {
    subscribe(){

    }
  };
  writeReady = {
    subscribe(){

    }
  };

  dismiss(){
    console.log('View Controller Dismiss Called');
  }
  _setHeader(){

  }
  _setNavbar(){

  }
  _setIONContent(){

  }
  _setIONContentRef(){

  }
}

Working on version:

Cordova CLI: 6.5.0 
Ionic Framework Version: 3.0.1
Ionic CLI Version: 3.0.0-beta7
ios-deploy version: 1.9.1 
ios-sim version: Not installed
OS: macOS Sierra
Node Version: v7.8.0
Xcode version: Xcode 8.3.2 Build version 8E2002

Upvotes: 16

Marky Sparky
Marky Sparky

Reputation: 61

I had the same issue when referring to ViewController in unit tests. I just solved it. Create a mock like this

class ViewControllerMock {
  public _setHeader(): any {
    return {}
  };
  public _setIONContent(): any {
    return {}
  };
  public _setIONContentRef(): any {
    return {}
  };
}

Then add it to your providers in the call to TestBed.configureTestingModule like this:

TestBed.configureTestingModule({
  declarations: [
    ...components,
    OrdinalPipe,
    IgnoreNulls
  ],
  providers: [
    NavController,
    ChartsService, FundsService, Utils, BlogService
    , Payment, PlanHelper, Storage, PalIdle, SimpleExpiry, ContentService, PlansService,
    App, Platform, Form, Keyboard, MenuController,
    { provide: ModalController, useClass: ModalControllerMock },
    { provide: ViewController, useClass: ViewControllerMock },
    { provide: Config, useClass: ConfigMock }
  ],
  imports: [
    FormsModule,
    IonicModule,
    ReactiveFormsModule,
  ],
})

This worked for me when I had the viewCtrl._setHeader is not a function error earlier today. Hope it helps.

Upvotes: 4

Related Questions