【问题标题】:Ionic 2 ViewController unit testingIonic 2 ViewController 单元测试
【发布时间】:2016-11-29 16:49:22
【问题描述】:

mIve 得到了以下单元测试来测试我用 Ionic 2 编写的组件。单元测试给出了来自 Ionic 库之一的错误,我假设我没有正确地模拟它或这样

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();
  })
})

这是使用正在测试的组件中的 ViewController 的相关摘录。

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

测试失败,我得到以下堆栈跟踪

  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)

这与我创建了一个茉莉花间谍的ViewController 行有关

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

查看代码库后,我在这里找到了 _setHeader 方法

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

我也尝试编写自定义提供程序,但也遇到了同样的错误。关于测试 ViewController 的正确方法的任何想法。

另外,有时在解决 ViewController 问题后,NavParams 可能会出现问题

【问题讨论】:

    标签: unit-testing angular jasmine karma-runner ionic2


    【解决方案1】:

    以 Marky Sparky 的回答为基础。从离子 3+ 开始:

    export class ViewControllerMock{
      readReady = {
        subscribe(){
    
        }
      };
      writeReady = {
        subscribe(){
    
        }
      };
    
      dismiss(){
        console.log('View Controller Dismiss Called');
      }
      _setHeader(){
    
      }
      _setNavbar(){
    
      }
      _setIONContent(){
    
      }
      _setIONContentRef(){
    
      }
    }
    

    正在处理版本:

    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
    

    【讨论】:

    • 从 beotung 开始,在 ionic 中也有内置模拟: import {mockApp, mockConfig, mockPlatform, mockView} from "ionic-angular/util/mock-providers";并像 {provide: ViewController, useValue: mockView()}, 一样使用它
    • ionic-angular/util/mock-providers 应该是一个答案,而不仅仅是一个评论
    【解决方案2】:

    在单元测试中引用 ViewController 时,我遇到了同样的问题。我刚刚解决了。 像这样创建一个模拟

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

    然后在调用 TestBed.configureTestingModule 时将其添加到您的提供程序,如下所示:

    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,
      ],
    })
    

    当我今天早些时候遇到 viewCtrl._setHeader is not a function 错误时,这对我有用。希望对您有所帮助。

    【讨论】:

    • 新的 Ionic 2.0 FINAL 打破了这个模拟。具体来说,这些函数调用 this._viewCtrlReadSub = viewCtrl.readReady.subscribe(function () { _this._viewCtrlReadSub.unsubscribe(); _this._readDimensions(); });
    【解决方案3】:

    已接受的答案不适用于 ionic 版本 3.9.2,但以下解决了该问题:

    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;
      }
    
    }
    

    离子信息

    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
    

    【讨论】:

      【解决方案4】:

      如果有人没有注意到 James Macmillan 的回答中的 cmets:

      @eesdil 写道:

      从 beotung 开始,在 ionic 中也有内置的模拟:import {mockApp,mockConfig,mockPlatform,mockView} 来自 “离子角/实用程序/模拟提供者”;并像{提供: ViewController,useValue: mockView()},2017 年 9 月 26 日 9:40


      这是对我有用的解决方案。

      【讨论】:

      • 这绝对是一个正确的答案(感谢 beotung)
      【解决方案5】:

      1. 创建 ViewController 的 Jasmine spy。

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

      2。在 providers 数组中使用viewCtrlSpy spy 如下:

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

      在这种情况下,监视比模拟更有效。

      【讨论】:

        【解决方案6】:

        添加

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

        提供

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-04-29
          • 1970-01-01
          • 1970-01-01
          • 2016-12-29
          • 2017-06-04
          • 2018-04-11
          • 2017-06-28
          相关资源
          最近更新 更多