美文网首页
Jest 简单介绍与使用

Jest 简单介绍与使用

作者: w晚风 | 来源:发表于2021-06-06 21:20 被阅读0次

    说明:我们的团队用的umi开发,所以下面会带点umi的代码,因为Umi框架内置了jest库 所以我们无需进行安装

    一 jest 常见断言 (测试匹配器)

    toBe

    toBe 匹配器有种类似于object.is或者===,精确相等。

    test('测试toBe', () => {
      // toBe为匹配器 matchers
      // toBe相当于js中的 object.is ===
      expect(10).toBe(10); // passed
    });
     
     
    test('测试toBe', () => {
      const a = {one: 1}
      expect(a).toBe( {one: 1});  // failed,因为两个对象的地址是不一样的
    });
    

    toEqual

    测试对象的内容是否相等,不比较对象的地址,只关心对象的内容是否一致,递归检查对象或数组的每个字段。

    test('测试toEqual', () => {
      const a = {one: 1}
      expect(a).toEqual( {one: 1});  // passed
    });
    

    toBeNull

    测试某个变量是否为null,如果是则Passed,否则failed

    test('测试toBeNull', () => {
      const a = null
      expect(a).toBeNull();  // passed
    });
    

    toBeUndefined 和 toBeDefined

    测试某个变量是否未定义,如果是则Passed,否则failed

    test('测试toBeUndefined', () => {
      const a = undefined;
      expect(a).toBeUndefined();  // passed
    });
     
    test('测试toBeUndefined', () => {
      const a = '';
      expect(a).toBeUndefined();  // failed
    });
     
    test('测试toBeUndefined', () => {
      const a = null;
      expect(a).toBeUndefined();  // failed
    });
    
    
    test('测试toBeDefined', () => {
      const a = null;
      expect(a).toBeDefined();  // passed
    });
     
    test('测试toBeDefined', () => {
      const a = undefined;
      expect(a).toBeDefined();  // failed
    });
    

    toBeTruthy

    测试某个变量是否为真,如果是则Passed,否则failed

    test('测试toBeTruthy', () => {
      const a = undefined;
      expect(a).toBeTruthy();  // undefined 视为false
    });
     
    test('测试toBeTruthy', () => {
      const a = null;
      expect(a).toBeTruthy();  // null视为false
    });
     
    test('测试toBeTruthy', () => {
      const a = 0;
      expect(a).toBeTruthy();  // 0 视为false
    });
     
    test('测试toBeTruthy', () => {
      const a = 1;
      expect(a).toBeTruthy();  // 1 视为true
    });
    

    toBeFalsy

    测试某个变量是否为假,如果是则Passed,否则failed

    test('测试toBeFalsy', () => {
      const a = 1;
      expect(a).toBeFalsy();  // failed,因为1 视为true
    });
     
    test('测试toBeFalsy', () => {
      const a = undefined;
      expect(a).toBeFalsy();  // passed,因为undefined 视为false
    });
     
    test('测试toBeFalsy', () => {
      const a = null;
      expect(a).toBeFalsy();  // passed,因为null 视为false
    });
     
    test('测试toBeFalsy', () => {
      const a = 0;
      expect(a).toBeFalsy();  // passed,因为0 视为false
    });
    
    test('测试toBeFalsy', () => {
      const a = 0;
      expect(a).not.toBeFalsy();  // failed,因为0 视为false,但是匹配器要的是真
    });
    

    数字相关

    test('测试toBeGreaterThan', () => {
      const count = 10;
      expect(count).toBeGreaterThan(9);  // passed,表示希望count这个变量的值比9大
    });
     
    test('测试toBeLessThan', () => {
      const count = 10;
      expect(count).toBeLessThan(9);  // failed,表示希望count这个变量的值比9小
    });
     
    test('测试toBeGreaterThanOrEqual', () => {
      const count = 9;
      expect(count).toBeGreaterThanOrEqual(9);   // passed,表示希望count这个变量的值大于等于9
    });
     
    test('测试toBeLessThanOrEqual', () => {
      const count = 9;
      expect(count).toBeLessThanOrEqual(9);   // passed,表示希望count这个变量的值小于等于9
    });
     
     
    test('测试toBeCloseTo', () => {
      const firstNumber = 0.1;
      const secondNumber = 0.2;
      expect(firstNumber + secondNumber).toEqual(0.3);  // 结果是failed,因为js计算浮点数的时
      expect(value).toBe(0.3);          //  这句会报错,因为浮点数有舍入误差候,有可能会溢出或者说不准确,这种情况下最好用toBeCloseTo
    });
     
    test('测试toBeCloseTo', () => {
      const firstNumber = 0.3;
      const secondNumber = 0.4;
      expect(firstNumber + secondNumber).toBeCloseTo(0.7);   // passed
    });
    

    字符串相关

    test('测试toMatch', () => {
      const str = 'www.baidu.com';
      expect(str).toMatch('baidu');   // passed, 表示str字符串中是否包含baidu这个字符串,是返回passed
      expect(str).toMatch(/baidu/); //passed,这里还可以写正则表达式
    });
    

    数组相关

    test('测试toContain', () => {
      const arr = ['dee', 'lee'];
      expect(arr).toContain('dee');   // passed, 表示arr数组中是否包含dee这个字符串元素,是返回passed
    });
     
    test('测试toContain', () => {
      const arr = ['dee', 'lee'];
      const data = new Set(arr);
      expect(data).toContain('dee');   // passed, 表示arr数组中是否包含dee这个字符串元素,是返回passed
    });
    

    异常情况

    const throwNewErrorFunc =  () => {
      throw new Error('this is a new error');
    }
     
    test('测试toThrow', () => {
      expect(throwNewErrorFunc).toThrow();   // passed, 表示希望throwNewErrorFunc这个方法运行的时候能够抛出一个异常
    });
     
    test('测试toThrow', () => {
      expect(throwNewErrorFunc).not.toThrow();   // failed, 表示希望throwNewErrorFunc这个方法运行的时候不能够抛出异常
    });
     
    test('测试toThrow', () => {
      expect(throwNewErrorFunc).toThrow('this is a new error');   // passed, 表示希望throwNewErrorFunc这个方法运行的时候能够抛出一个异常,并且内容是'this is a new error'
      expect(throwNewErrorFunc).toThrow(/this is a new error/); // 也可以是正则表达式
    });
    

    二 测试案例

    针对方法的测试:

    1.测试方法的两个案例

    1.1

    src目录下新建一个common/index.js开始添加一个简单的方法 代码如下:

    function add(a,b){
       return a+b;
    }
    function minnus(a,b) {
       return a-b;
    }
    module.exports={
       add,
       minnus
    }
    

    在跟目录新建目录 test/common/index.test.js 用于自动测试index.js 中的方法 代码如下:

    import { add , minnus } from '../../src/common/index'
    
    // 尽量每次编写测试用例都用describe包裹进行分块
    // 每个测试用例一个it函数代表
    // 参数:
    // 字符串,代表测试用例名称:常用命名模式“被测对象在什么情况下是什么行为”
    // 函数,实际测试用例过程
    describe('测试common/index 文件相关代码', () => {
      // 测试用例
      it('调用 add方法执行 1+1=2',()=>{
        // 测试调用后的预期值为2
        expect(add(1,1)).toBe(2)
      })
      it('调用 minnus方法 执行1-1=0',()=>{
        // 测试调用后的预期值为0
        expect(minnus(1,1)).toBe(0)
      })
    })
    
    

    1.2

    开发代码

    export default class Counter{
        constructor(){
            this.number = 0 ;
        }
        addOne(){
            this.number += 1;
        }
        minusOne(){
            this.number -= 1;
        }
    }
    

    test测试文件

    
    import  Counter  from '../../src/common/index'
    const counter = new Counter();
    
    describe('测试common/index 文件相关代码', () => {
      it('测试counter中addOne方法',()=>{
          counter.addOne(); // 调用addOne方法后 number 执行了 +1 正确结果为1
          expect(counter.number).toBe(1) // 表示number值的正确结果为1
      })
      it('测试counter中minusOne方法',()=>{
          counter.minusOne(); // 调用addOne方法后 number 执行了 - 1, 而上面执行了addOne()方法  正确结果为 0
          expect(counter.number).toBe(0) // 表示number值的正确结果为 0
      })
    })
    

    [图片上传失败...(image-e7c678-1622985585990)]

    2.测试异步代码

    这是非常常见的通用处理方式,比如你有一个fetchData(callback)的function用来获取数据,并且在获取完成的时候调用callback 函数,你想测试返回的数据是“peanut butter” ,默认情况下当fetchData执行完成的时候Jest的测试就完成了,这并不是你所期望的那样的去运行。

    方法一:回调函数

    代码示例:fetechData.js文件代码

    import request from "umi-request";
    export const fetchData = (fn:any)=>{
        request.get("http://mock-api.com/RKDx59Ka.mock/test").then((response)=> {
            fn(response.data) 
        })
    }
    

    对应的测试用例fetechData.test.js文件代码

    import  { fetchData}  from '../../src/common/fetechData'
    
    test('测试 fetchData返回结果为{list: [],state: false}',(done)=>{
      fetchData((data)=>{
            // 对比返回的 data数据是否一致
          expect(data).toEqual({
              list: [],
                state: false
        })
        done(); // 回调函数,测试用例执行完成执行done 
      })
    })
    

    方法二:使用Promise方法

    代码示例:fetechData.js文件代码

    import request from "umi-request";
    export const fetchData = ()=>{
        return request.get("http://mock-api.com/RKDx59Ka.mock/test")
    }
    

    对应的测试用例fetechData.test.js文件代码

    import  { fetchData}  from '../../src/common/fetechData'
    
    test('测试 fetchData返回结果为{list: [],state: false}',()=>{
      return fetchData().then((response)=>{
        expect(response.data).toEqual({
              list: [],state: false
        })
      })
    })
    

    如果要测试404效果可以这样 故意把接口地址改成没用的地址
    对应的测试用例fetechData.test.js文件代码

    test('测试 fetchData返回结果 404',()=>{
        return fetchData().catch(e=>{
            console.log('e',e);
            expect(e.toString().indexOf('404')>-1).toBe(true)
        })
    })
    

    方法三:使用 Async/Await

    我相信大家对Async/Await 是比较熟悉的,你可以在测试中使用异步和等待。要编写一个async测试,只需在传递到测试的函数前面使用async关键字。例如上面同样的fetchData场景可以使用下面的实现:

    对应的测试用例fetechData.test.js文件代码

    import  { fetchData}  from '../../src/common/fetechData'
    
    test('测试 fetchData返回结果为{ code: 0, data: { list: [], state: false } }',async ()=>{
        let obj;
        await fetchData().then((res)=>{
            obj = res;
        })
        const obj2 = { code: 0, data: { list: [], state: false } }
        // tomatchobject检查一个JavaScript对象是否匹配一个对象的属性子集
        expect(obj).toMatchObject(obj2);
    })
    

    3.Jest中钩子函数

    在jest中,如果测试用例中需要使用到某个对象 或 在执行测试代码的某个时刻需要做一些必要的处理,直接在测试文件中写基础代码是不推荐的,可以使用jest的钩子函数。

    钩子函数概念:在代码执行的某个时刻,会自动运行的一个函数。

    demo2 上写的 的测试用例可以正常执行,但是addOne()函数调用的次数会影响counter.number的值,就会影响到minusOne方法执行结果。如果你想addOne与minusOne方法调用互不影响时,此时就不得不引入jest的钩子函数。

    beforeAll

    在所有测试用例执行之前执行一次

    
    import  Counter  from '../../src/common/index'
    let counter = null;
    
    beforeAll(()=>{
        console.log('beforeAll')
        counter = new Counter();
    })
    test('测试counter中addOne方法',()=>{
        counter.addOne();
        expect(counter.number).toBe(1)
    })
    test('测试counter中minusOne方法',()=>{
        counter.minusOne();
        expect(counter.number).toBe(0)
    })
    

    注意:使用beforeAll时,需要把之前const类型修改成let 。此时还是会相互影响,依旧不能达成目标。

    beforeEach

    在每个测试用例执行之前执行一次

    
    import  Counter  from '../../src/common/index'
    let counter = null;
    
    beforeAll(()=>{
        console.log('beforeAll')
    })
    beforeEach(()=>{
        console.log('beforeEach')
        counter = new Counter();
        console.log('counter.number',counter.number)
    })
    test('测试counter中addOne方法',()=>{
        counter.addOne();
        expect(counter.number).toBe(1)
    })
    test('测试counter中minusOne方法',()=>{
        counter.minusOne();
        expect(counter.number).toBe(0)
    })
    

    此时直接执行npm run test命令时会有一个用例执行失败,执行结果如图所示:

    1.jpg

    此时返回的结果已经不是0了 ,而是-1!此时addOne 与minusOne两个测试用例已经不会相互影响了。每次执行测试用例前,counter都会初始化!

    afterAll

    在所有测试用例执行完之后执行一次

    afterEach

    在每个测试用例执行完成之后执行一次

    
    import  Counter  from '../../src/common/index'
    let counter = null;
    
    // 在所有测试用例执行之前执行一次
    beforeAll(()=>{
        console.log('beforeAll')
        counter = new Counter();
        console.log('counter.number',counter.number)
    })
    
    // 在每个测试用例执行之前执行一次
    beforeEach(()=>{
      console.log('beforeEach')
      counter = new Counter();
    })
    
    // 在每个测试用例执行完成之后执行一次
    afterEach(()=>{
        console.log('afterEach')
    })
    
    //在所有测试用例执行完之后执行一次
    afterAll(()=>{
      console.log('afterAll')
    })
    
    test('测试counter中addOne方法',()=>{
        counter.addOne();
        expect(counter.number).toBe(1)
    })
    test('测试counter中minusOne方法',()=>{
        counter.minusOne();
        expect(counter.number).toBe(-1)
    })
    

    钩子函数执行顺序:

    根据上边案例实际打印结果可以看出这四个钩子函数的执行顺序,如下:

    (1)beforeAll > (2)beforeEach > (3)afterEach > (4)afterAll

    靠前的一次优先执行。

    4.snapshot快照测试

    1、快照测试

    项目中经常有一些配置文件。比如

    export const generateConfig = ()=>{ return { server :'http://localhost', port:'8080', domain:'localhost' } }
    

    对应它的测试用例可以这样写 snapshot.test.js

    import  { generateConfig}  from '../../src/common/snapshot'
    
    test('测试 generateConfig', () => {
        expect(generateConfig()).toEqual({
            server: 'http://localhost',
            port: '8080',
            domain:'localhost'
        })
    })
    

    当配置项不断增加的时候,就需要不断去更改测试用例。

    那么我们可以使用快照写测试用例:

    import  { generateConfig}  from '../../src/common/snapshot'
    
    test('测试 generateConfig', () => {
        expect(generateConfig()).toMatchSnapshot()
    })
    

    运行测试用例之后会自动生成快照文件,对应目录如下:  


    3.jpg

    toMatchSnapshot() 会为expect 的结果做一个快照并与前面的快照做匹配。(如果前面没有快照那就保存当前生成的快照即可)

    这在配置文件的测试的时候是很有用的,因为配置文件,一般不需要变化。

    当然,确实要改配置文件,然后要更新快照,也可。

    2、配置文件修改

    如果配置文件发生变化,运行时会提示已经发生变化,可以根据提示的命令确定修改文件:‘npm test -- -u’

    ![5.jpg](https://img.haomeiwen.com/i11226018/b76f60c3b0890cac.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

    如果添加new Date()这样的参数,配置文件在实时的更新,如何处理呢?对应代码如下:
    snapshot.js

    export const generateConfig  = () => {
        return {
            server: 'http://localhost',
            port: '8080',
            domain:'localhost',
            time:new Date()
        }
    }
    

    测试用例snapshot.test.js

    import  { generateConfig}  from '../../src/common/snapshot'
    
    test('测试 generateConfig', () => {
        expect(generateConfig()).toMatchSnapshot({
            time :expect.any(Date)// Date String Number等等
        })
    })
    

    3、行内快照

    先需要安装 prettier ,否则行内快照无法执行。

    安装命令:npm install prettier

    安装完成之后,修改测试用例:

    import  { generateConfig}  from '../../src/common/snapshot'
    
    test('测试 generateConfig', () => {
        expect(generateConfig()).toMatchInlineSnapshot({
            time :expect.any(Date)// Date String Number等等
        })
    })
    

    运行测试用例之后,会多出一个参数来,表示不匹配 结果如下:

    import { generateConfig } from '../../src/common/snapshot';
    
    test('测试 generateConfig', () => {
      expect(generateConfig()).toMatchInlineSnapshot(
        {
          time: expect.any(Date), // Date String Number等等
        },
        `
        Object {
          "domain": "localhost",
          "port": "8080",
          "server": "http://localhost",
          "time": Any<Date>,
        }
      `,
      );
    });
    

    相关文章

      网友评论

          本文标题:Jest 简单介绍与使用

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