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

Jest 简单介绍与使用

作者: 头发飘逸 | 来源:发表于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