4

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

6 Answers 6

16

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
Sign up to request clarification or add additional context in comments.

2 Comments

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()},
ionic-angular/util/mock-providers should be an answer and not just a comment
4

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.

1 Comment

the new Ionic 2.0 FINAL breaks this mock. Specifically, these function calls this._viewCtrlReadSub = viewCtrl.readReady.subscribe(function () { _this._viewCtrlReadSub.unsubscribe(); _this._readDimensions(); });
1

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

Comments

1

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.

1 Comment

definitely this is a correct answer (with kudos to beenotung)
0

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.

Comments

0

add

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

to provides

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.