美文网首页我爱编程
ionic 单元测试

ionic 单元测试

作者: 壹点微尘 | 来源:发表于2018-03-13 09:03 被阅读601次
    Angular项目创建时,会自动生成spec.ts单元测试文件. 在ionic项目中, 需要手动创建spec.ts文件和添加依赖的框架.

    添加配置依赖可以参看ionic-team写的一篇文章 :点我😄

    单元测试基础入门

    简单示例
    describe('A suite is just function',()=>{
      var a;
      it('and so is a spec ', function () {
        a = true;
        expect(a).toBe(true);
      });
    })
    

    执行结果:


    a 等于 true, 用例测试成功

    上面的例子是一个很简单的单元测试, 它有三部分组成: describe() , it()expect()组成;

    编写单元测试用例的基本步骤:
    • 设计测试用例函数: 根据测试场景组合使用describe()it()来定义测试用例;
    • 编写判断逻辑: 在it()函数中, 需要使用expect()断言函数来判定用例测试结果. 例如, expect(a).toBe(true);表示变量a应等于true,否则说明这个用例测试失败.
      a 等于 false, 则用例测试失败
    测试集

    Jasmine中, 将功能相似的测试用例统一聚集在测试集(Suites)中, 并通过describe()函数标识. 假设被测试的业务类有多个函数, 则需要编写多个测试用例, 并将这些测试用例都放在同个 describe()函数中.

    测试点

    每个具体的功能测试点 (Spec) 可以用全局函数it()来定义, 该函数的第一个参数表示该用例的名称, 第二个参数为测试的细节. it()函数可以包含一个或者多个断言 (expect), 每个断言的结果只能是 true或者false, 只有当所有断言都为true是, 该测试点才算通过.

    内置匹配器

    内置匹配器(Matchers), 就是更在expect()函数后面, 用于判断结果是否符合期望值的函数. 在上面的例子中, 已经使用了一个很常用的匹配器toBe().

    内置常用匹配器

    • toBe()

    toBe()本质是使用操作符 ===来比较结果值和期望值, not.toBe()则表示不等, 示例代码如下:

      it("The 'toBe()' matcher compares with '===' ", function () {
        var a = 15;
        var b = a;
        expect(a).toBe(b);
        expect(a).not.toBe(null);
      });
    
    • toEqual()
      toEqual()用于比较两个对象、数字、字符串等, 示例代码如下:
      it('should work for objects', function () {
        var foo = {
          a: 8,
          b: 15
        };
    
        var bar = {
          a: 8,
          b: 15
        }
    
        expect(foo).toEqual(bar);
      });
    
    • toMatch()
      toMatch()用于验证是否匹配正则表达式, 示例代码如下:
      it("The 'toMatch' matcher is for regular expressions", function () {
        var message = 'Ionic CLI and Cordova';
        expect(message).toMatch(/ionic/i);
      });
    
    • toContain()
      toContain()用于验证数组是否包含指定元素, 示例代码如下:
      it("The 'toContain' matcher is for finding an item in an array", function () {
        var array = ['React', 'Vue', 'Angular'];
        expect(array).toContain('Angular');
      });
    
    Jasmine 内置匹配器:
    匹配器 作用
    toBe 使用 === 比较结果
    toEqual 比较两个对象、数字、字符串等是否相等
    toMatch 正则表达式匹配
    toBeNull 验证是否为null
    toBeTruthy 验证是否为 true
    toBeFalsy 验证是否为 false
    toBeLessThan 验证结果是否小于指定值
    toBeGreaterThan 验证结果是否大于指定值
    toContain 验证数组是否包含指定元素
    toBeCloseTo 将值进行四舍五入后比较是否相等
    toThrow 验证函数是否抛出一个错误
    toThrowError 验证函数是否抛出指定的错误
    toBeDefined 验证对象是否 不为 undefined
    beforeEach 、afterEach函数

    另外, 在测试中还会经常用到 beforeEach()afterEach() 这两个函数, 用于定义每个测试用例执行前及执行后的公共逻辑 。

    • beforeEach() : 定义了每个用例在执行前, 所需要执行的初始化函数。
    • afterEach (): 每个用例在执行结束后, 均将执行afterEach ()函数。

    beforeEach()的简单使用:

    describe('How to use beforeEach', () => {
    
      let foo, bar;
      beforeEach(() => {
        foo = {
          a: 8,
          b: 15
        };
    
        bar = {
          a: 8,
          b: 15
        }
      })
    
      it('should work for objects', function () {
        expect(foo).toEqual(bar);
      });
    })
    

    单元测试

    • 组件的测试
      page3.html:
      <ion-content no-padding>
        <ion-list>
          <button ion-item *ngFor="let item of list">
            {{item}}
          </button>
        </ion-list>
      </ion-content>
      
      page3.ts:
      export class Page3 {
      
        list: string[] = ['张三', '李四', '王五'];
      
        constructor() {}
        ionViewDidLoad() {}
      
        addItem() {
          this.list.push(`赵六`);
        }
      }
      
      page3.spec.ts
      import {Page3} from "./page3";
      
      describe('Page3 组件', () => {
        let component;
        beforeEach(() => {
          component = new Page3();
        });
      
        it("调用 addItem() 函数, 并检测该函数是否添加了 '赵六'", function() {
          component.addItem();
          expect(component.list).toContain('赵六');
        });
      })
      
    • 管道的测试
      要求: 创建一个pipe, 把手机号(15912345678)显示为159-1234-5678的形式,并对该pipe做单元测试。
      ll-phone.ts
      import {Pipe, PipeTransform} from '@angular/core';
      
      @Pipe({
        name: 'llPhone',
      })
      export class LlPhonePipe implements PipeTransform {
      
        transform(value: string): string {
          if (!value) return '';
          if (value.length === 11) {
            return value.replace(/(\d{3})(\d{4})(\d{4})/, (m, m1, m2, m3) =>     {
              return [m1, m2, m3].join('-');
            })
          } else {
            return value;
          }
        }
      }
      
      ll-phone.spec.ts:
      import {LlPhonePipe} from "./ll-phone";
      
      describe('测试 llPhonePipe', () => {
      
        let phonePipe: LlPhonePipe;
      
        beforeEach(() => {
          phonePipe = new LlPhonePipe();
        });
      
        it('should transforms phone', function () {
          expect(phonePipe.transform('15912345678')).toEqual('159-1234-5678');
        });
      })
      
    • 服务的测试
      创建一个MockDataLlMockDataProvider
      ll-mock-data.ts:
      import {Injectable} from '@angular/core';
      
      @Injectable()
      export class LlMockDataProvider {
        constructor() {}
        public hotMenu = [{
          "img": "http://file.j1home.com/%E5%AE%89%E5%85%A8%E7%A4%BE%E5%8C%BA@2x.png",
          "name": "社区安全"
        }, {
          "img": "http://file.j1home.com/%E7%A4%BE%E5%8C%BA%E5%85%9A%E5%BB%BA@2x.png",
          "name": "社区党建"
        }];
      }
      
      ll-mock-data.spec.ts:
      import {inject, TestBed} from "@angular/core/testing";
      import {LlMockDataProvider} from "./ll-mock-data";
      
      describe('test provider', () => {
        beforeEach(() => {
          TestBed.configureTestingModule({
            providers: [LlMockDataProvider]
          });
        });
      
        it('should contain item', inject([LlMockDataProvider], (service) => {
          expect(service.hotMenu[1]['name']).toEqual('社区党建');
        }));
      })
      

    通过TestBed注入被测类

    在上面的代码ll-mock-data.spec.ts中,我们使用到了TestBed.configureTestingModule(),它为我们创建了一个测试 NgModule,在它的元数据里面,我们可以注入被测试的类。如果有依赖,比如:Http,那么你需要 imports: [HttpModule],这一切都跟 NgModule 写法完全一置。

    beforeEach(() => {
        TestBed.configureTestingModule({
          providers: [LlMockDataProvider]
        });
    });
    

    组件 DOM 交互测试

    在下面的图片中, 如果想检测DOM元素的内容是否符合我们的要求,
    可通过TestBed进行组件的初始化, 可以获取组件模板对应的DOM元素以及检查 DOM元素的内容等。

    示例代码如下:
    page3.html

    <ion-content no-padding>
      <ion-list>
        <button ion-item *ngFor="let item of list">
          {{item | llPhone}}
        </button>
      </ion-list>
    </ion-content>
    

    page3.ts

    import {Component} from '@angular/core';
    import {IonicPage, NavController, NavParams} from 'ionic-angular';
    
    @IonicPage()
    @Component({
      selector: 'page-page3',
      templateUrl: 'page3.html',
    })
    export class Page3 {
      list: string[] = ['15512345678'];
      constructor() {}
      ionViewDidLoad() {}
      addItem() {
        this.list.push(`15512345678`);
      }
    }
    

    page3.spec.ts

    import {TestBed} from "@angular/core/testing";
    import {Page3} from "./page3";
    import {IonicModule} from "ionic-angular";
    import {LlPhonePipe} from "../../pipes/ll-phone/ll-phone";
    
    describe('Page3 组件', () => {
    
      beforeEach(() => {
        TestBed.configureTestingModule({
          declarations: [Page3, LlPhonePipe],
          imports: [
            IonicModule.forRoot(Page3)
          ],
        });
      });
    
      it('test simple component with TestComponentBuilder', () => {
        var fixture = TestBed.createComponent(Page3);
        var compiled = fixture.debugElement.nativeElement;
    
        // 调用 组件的 addItem() 函数
        fixture.componentInstance.addItem();
        // 变化监测
        fixture.detectChanges();
    
        // replace(/(^\s*)|(\s*$)/g, "") 移除字符串首尾空格
        let val = compiled.querySelector('.label').textContent.replace(/(^\s*)|(\s*$)/g, "");
        expect(val).toBe('155-1234-5678');
      });
    });
    

    在上面的代码中, 需要关注以下的知识点:

    • TestBedcreateComponent()方法用于初始化被测组件, 并创建 ComponentFixture对象, 这个对象可以认为是被测组件的上下文环境, 通过它可以获取已经完成初始化的组件实例以及DOM元素等。
    • fixture.detectChanges()方法用于当JavaScript变量及模板内容变更时触发变化监测。组件初始化、DOM元素或JavaScript值变化时,都需要调用这个方法, 以便触发 Angular的变化监测机制。
    • fixture.debugElement.nativeElement用于获取组件对应的原生DOM元素, 之后可以通过querySelector()DOM元素原生 API做进一步处理。
    • fixture.componentInstance用于获取组件对应的对象实例,上面代码中通过fixture.componentInstance调用该实例对象的addItem()函数: fixture.componentInstance.addItem();

    相关文章

      网友评论

        本文标题:ionic 单元测试

        本文链接:https://www.haomeiwen.com/subject/pyfnfftx.html