zoukankan      html  css  js  c++  java
  • 前端angular基础测试篇

    jest提供覆盖率报告等开发者所需要的所有测试工具,jest是一款几乎零配置的 测试框架。angular jest单元测试的写法为三步,引入测试内容,运行测试内容, 最后进行比较,是否达到预期。jest中的断言使用expect, 它接受一个参数,就 是运行测试内容的结果,返回一个对象,这个对象来调用匹配器 (toBe) ,匹配器的参数就是我们的预期结果,这样就可以对结果和预 期进行对比了,也就可以判断对不对了。

    两个必会的方法test方法:Jest封装的测试方法,一般填写两个参数,描述和测 试方法,expect方法 :预期方法,就是你调用了什么方法,传递了什么参数,得 到的预期是什么。

    1 常用jest断言

    • toBe:绝对相等(=)
    // 在src/functions.js中创建被测试的模块
    export default {
      sum(a, b) {
        return a + b;
      }
    }
    
    // 在test/functions.test.js文件中创建测试用例
    import functions  from '../src/functions';
    
    test('sum(2 + 2) 等于 4', () => {
      expect(functions.sum(2, 2)).toBe(4);
    })
    
    • not:允许你测试结果不等于某个值的情况
    // 在src/functions.js中创建被测试的模块
    export default {
      sum(a, b) {
        return a + b;
      }
    }
    
    // functions.test.js
    import functions  from '../src/functions'
    
    test('sum(2, 2) 不等于 5', () => {
      expect(functions.sum(2, 2)).not.toBe(5);
    })
    
    • toEqual:简单类型绝对匹配;复杂类型内容结果的匹配
    // functions.js
    export default {
      getAuthor() {
        return {
          name: 'LITANGHUI',
          age: 24,
        }
      }
    }
    // functions.test.js
    import functions  from '../src/functions';
    
    test('getAuthor()返回的对象深度相等', () => {
      expect(functions.getAuthor()).toEqual(functions.getAuthor());
    })
    
    test('getAuthor()返回的对象内存地址不同', () => {
      expect(functions.getAuthor()).not.toBe(functions.getAuthor());
    })
    
    • toHaveLength:方便的用来测试字符串和数组类型的长度是否满足预期
    // functions.js
    export default {
      getIntArray(num) {
        if (!Number.isInteger(num)) {
          throw Error('"getIntArray"只接受整数类型的参数');
        }
    
        let result = [];
        for (let i = 0, len = num; i < len; i++) {
          result.push(i);
        }
        
        return result;
      }
    }
    // functions.test.js
    import functions  from '../src/functions';
    
    test('getIntArray(3)返回的数组长度应该为3', () => {
      expect(functions.getIntArray(3)).toHaveLength(3);
    })
    
    • toThorw:可能够让我们测试被测试方法是否按照预期抛出异常,但是在使用时 需要注意的是:我们必须使用一个函数将将被测试的函数做一个包装,正如上 面getIntArrayWrapFn所做的那样,否则会因为函数抛出导致该断言失败
    // functions.test.js
    import functions  from '../src/functions';
    
    test('getIntArray(3.3)应该抛出错误', () => {
      function getIntArrayWrapFn() {
        functions.getIntArray(3.3);
      }
      expect(getIntArrayWrapFn).toThrow('"getIntArray"只接受整数类型的参数');
    mockImplementation})
    
    • toBeNull():匹配null
    • toBeUndefined():匹配undefined
    • toBeDefined():匹配非undefined
    • toBeTruthy():匹配转化后为true
    • toBeFalsy():匹配转化后为false
    • toBeGreaterThan():相当于大于号
    • toBeLessThan():相当于小于号
    • toBeGreaterThanOrEqual():相当于大于等于号
    • toBeLessThanOrEqual():相当于小于等于号
    • toBeCloseTo():解决js浮点错误
    • toMatch(regExp/string):用正则表达式或者字符串匹配字符串片段
    • toContain():匹配数组或者Set中的某一项

    2 mock函数

    进行单元测试时,要测试的内容依赖其他内容,比如异步请求,会依赖网络,很 可能造成测试达不到效果。能不能把依赖变成可控的内容?这就用到mack。mack 就是把依赖替换成我们可控的内容,实现测试的内容和它的依赖项隔离。那怎么 才能实现mock呢?使用mack 函数。在jest中,当我们谈论mack的时候,其实谈 论的就是使用mack函数代替依赖。mack函数就是一个虚拟的或假的函数,所以对 它来说,最重要的就是实现依赖的全部功能,从而起到替换的作用。通常,mock 函数会提供以下三个功能,来实现替换:函数的调用捕获,设置函数返回值,改 变原函数的实现。在jest 创建一个mock 函数最简单的方法就是调用jest.fn() 方法。

    2.1 函数的调用捕捉

    捕获调用指的是这个函数有没有被调用,调用的参数是什么,返回值是什么,通 常用于测试回调函数,模拟真实的回调函数。

    // functions.js
    export default {
      forEachFun: (array: any[], callback: Function) => {
        array.forEach((i) => callback(i));
      }
    }
    // functions.test.js
    import functions  from '../src/functions';
    test('forEachFun调用每个方法', () => {
      const mockFun = jest.fn();
      const testArr = [1, 2];
      functions.forEachFun(testArr, mockFun);
      console.log(mockFun.mock);
      // expect(mockFun.mock.calls.length).toBe(2);
      expect(mockFun).toHaveBeenCalled();
      expect(mockFun).toBeCalledTimes(2);
      expect(mockFun).toBeCalledWith(1);
      expect(mockFun).toBeCalledWith(2);
    });
    // mock函数mockFun属性是一个对象,打印结果:
    {
      calls: [ [ 1 ], [ 2 ] ],
      instances: [ undefined, undefined ],
      invocationCallOrder: [ 1, 2 ],
      results:[ 
            { type: 'return', value: undefined },
            { type: 'return', value: undefined }
      ] 
    }
    

    calls 保存的就是调用状态。calls 是一个数组,每一次的调用都组成数组的 一个元素,在这里调用了两次,就有两个元素。每一个元素又是一个数组,它 则表示的是函数调用时的参数,因为每次的调用都传递了一个参数给函数,所 以数组只有一项。如果有多个参数,数组就有多项,按照函数中的参数列表依 次排列。这时候,就可以做断言,函数调用了几次,就判断 calls.length. expect(mockFun.mock.calls.length).toBe(2) 就是断言函数 是不是调用了两次。expcet(mockFun.mock.calls[0][0]) .toBe(1)就是断言第 一次调用的时候传递的参数是不是1. 可能觉得麻烦了, 的确有点麻烦了,幸 好,jest 对函数的mock参数进行了简单的封装,提供了简单的匹配器。

    // 用来判断mock函数是否被掉用过
    toHaveBeenCalled()/toBeCalled()
    // 用来判断mock函数调用过几次
    toHaveBeenCalledTimes(number)/toBeCalledTimes(number)
    // 用来判断是否使用了特定参数调mock函数
    toHaveBeenCalledWith(arg1,arg2,...)/toBeCalledWith(arg1,arg2,...)
    

    有的时候,由于后端没有开发好,或网络问题,不想调用函数,直接获取到函数 的返回值就可以了,比如异步函数, 直接返回一个Observable就好了,根本没有 必要请求服务器。

    test('设置函数返回值', () => {
      // 普通返回
      const mockFun = jest.fn();
      mockFun.
      const result: string = mockFun();
      expect(mockFun).toBeCalledTimes(1);
      expect(result).toEqual({name: 'shao'});
      // observable返回
      const observableFun = jest.fn();
      observableFun.mockReturnValue(of({name: 'shao'}));
      const observableResult: Observable<any> = observableFun();
      observableResult.subscribe((res) => {
        expect(res).toEqual({name: 'shao'});
      });
    });
    

    2.2 使用spyOn间谍测试服务

    服务往往是最容易进行单元测试的文件。下面是一些针对 ValueService 的同步 和异步单元测试,甚至不需要 Angular 测试工具的帮助。

    // Straight Jasmine testing without Angular's testing support
    describe('ValueService', () => {
      let service: ValueService;
      beforeEach(() => { service = new ValueService(); });
    
      it('#getValue should return real value', () => {
        expect(service.getValue()).toBe('real value');
      });
    
      it('#getObservableValue should return value from observable',
        () => {
        service.getObservableValue().subscribe(value => {
          expect(value).toBe('observable value');
        });
      });
    });
    

    服务通常依赖于angular 在构造函数中注入的其它服务。在很多情况下,调用 服务的构造函数时,很容易手动创建和注入这些依赖。

    // ValueService.ts
    @Injectable({
      providedIn: 'root'
    })
    export class ValueService {
      value = 'real value';
    
      getValue(): string { return this.value; }
      setValue(value: string): void { this.value = value; }
    
      getObservableValue(): Observable<string> { return of('observable value'); }
    
      getPromiseValue(): Promise<string> { return Promise.resolve('promise value'); }
    
      getObservableDelayValue(): Observable<string> {
        return of('observable delay value').pipe(delay(10));
      }
    }
    
    // ValueService.spec.ts
    export class FakeValueService extends ValueService {
      value = 'faked service value';
    }
    
    describe('MasterService without angular testing support', () => {
      let masterService: MasterService;
    
      it('#getValue 返回 service', () => {
        masterService = new MasterService(new ValueService());
        expect(masterService.getValue()).toBe('real value');
      });
    
      it('#getValue 使用 fakeServiece', () => {
        masterService = new MasterService(new FakeValueService());
        expect(masterService.getValue()).toBe('faked service value');
      });
    
      it('#getValue 使用 fake object', () => {
        const fake = { getValue: () => 'fake value' };
        masterService = new MasterService(fake as ValueService);
        expect(masterService.getValue()).toBe('fake value');
      });
    
      it('#getValue 返回 by a spy', () => {
        const valueService: ValueService = new ValueService();
        jest.spyOn(valueService, 'getValue').mockReturnValueOnce('test');
        masterService = new MasterService(valueService);
        expect(masterService.getValue()).toBe('test');
      });
    });
    

    第一个测试使用 new 创建了一个 ValueService,并把它传给了 MasterService 的构造函数。然而,注入真实服务很难工作良好,因为大多数被依赖的服务都很 难创建和控制。还可以模拟依赖、使用仿制品,或者在相关的服务方法上创建一 个测试间谍spyOn。

    2.3 angular TestBed

    TestBed 是 Angular 测试实用工具中最重要的。 TestBed 创建了一个动态构造 的 Angular 测试模块,用来模拟一个 Angular 的 @NgModule 。 TestBed.configureTestingModule() 方法接受一个元数据对象,它可以拥有 @NgModule的大部分属性。要测试某个服务,你可以在元数据属性 providers 中 设置一个要测试或模拟的服务数组。

    let masterService: MasterService;
    let valueService: ValueService;
    
    beforeEach(() => {
      TestBed.configureTestingModule({
        providers: [
          MasterService, ValueService
        ]
      });
      masterService = TestBed.inject(MasterService);
      valueService = TestBed.inject(ValueService);
    });
    

    测试带依赖的服务时,同时使用spyOn:

    // value.service.ts
    @Injectable()
    export class MasterService {
      constructor(private valueService: ValueService) { }
      getValue(): string {
        return this.valueService.getValue();
      }
    }
    // value.service.spec.ts
    describe('MasterService动态构建', () => {
      let masterService: MasterService;
      let valueService: ValueService;
    
      beforeEach(() => {
        TestBed.configureTestingModule({
          providers: [
            MasterService, ValueService
          ]
        });
        masterService = TestBed.inject(MasterService);
        valueService = TestBed.inject(ValueService);
      });
    
      it('#getValue从spy返回', () => {
        const stubValue = 'stub value';
        jest.spyOn(valueService, 'getValue').mockReturnValue(stubValue);
        expect(masterService.getValue()).toBe(stubValue);
        expect(valueService.getValue).toBeCalledTimes(1);
        window.console.log(valueService.getValue);
        expect(valueService.getValue()).toBe(stubValue);
      });
    });
    

    Author: flysic

    Created: 2021-10-24 Sun 22:26

    Validate

  • 相关阅读:
    即时通信 选择UDP还是TCP协议
    Gradle 在Eclipse中的使用
    使用idea+gradle建立SSM项目
    Gradle安装和在IDEA使用 基本操作
    IDEA配置 gradle
    Trustin Lee
    java.security.MessageDigest (2) 生成安全令牌!
    java.security.MessageDigest (1)
    递归算法结合数据库 解析 java树形结构
    mysql 日期加减
  • 原文地址:https://www.cnblogs.com/machine/p/15456062.html
Copyright © 2011-2022 走看看