前言
通过自动化测试可以了解当前代码能否达到预期结果,如果逻辑有缺陷,可以快速的定位bug;如果是开发类库,可以避免新添加的逻辑影响之前的内容;同时也可以提高代码的可维护性。
jest是facebook推出的,它基于jsdom,用js对象模拟浏览器环境,默认具备断言库、chai、sinon等,零配置实现自动化测试,但是不能测试样式相关内容
js测试
语法describe,表示套件;it(测试用例)
// parser.js
const parser = (str) => {
const obj = {}
str.replace(/([^&=]+)=([^&=]+)/g, function() {
obj[arguments[1]] = arguments[2]
})
return obj
}
// parser.test.js
describe('测试parser', () => {
it('测试parser是否能正常解析', () => {
expect(parser('a=1&b=2')).toEqual({
a: "1",
b: "2"
})
})
})
测试结果类型
it('测试相等 全等 长得一样 是不是真的 是不是假的', () => {
expect(1+1).toBe(2)
expect({name: "mm"}).toEqual({name: "mm"})
expect(true).toBeTruthy()
expect(false).toBeFalsy
});
it('测试不相等 大于 小于 大于等于 小于等于', () => {
expect(1+1).not.toBe(3)
expect(1+1).toBeLessThan(3)
expect(1+1).toBeGreaterThan(1)
});
it('是否包含 是否匹配', () => {
expect('hello').toContain('h')// toContain的参数是字符串
expect('hello').toMatch(/hello/); // toMatch的参数是正则
});
// it.only只测试当前文件的这个用例
it.only('是否包含 是否匹配', () => {
expect('hello').toContain('h')
expect('hello').toMatch(/hello/);
});
dom测试
jest在node环境下自己模拟了一套dom的api, 叫做jsDom,用于支持dom测试
// dom.js
const addNode = (node, parent) => {
parent.appendChild(node)
}
// dom.test.js
it('测试能否正常添加节点', () => {
document.body.innerHTML = '<div id="wrapper"></div>'
let button = document.createElement('button');
let wrapper = document.querySelector('#wrapper');
addNode(button, wrapper)
let btn = wrapper.querySelector('button')
expect(btn).not.toBeNull()
})
async测试
done函数
默认情况下,Jest测试一旦执行到末尾就会完成,而不会执行cb中的内容,即不会等待异步执行完毕再测试;要解决这个问题,需要使用单个参数调用done;Jest会等done函数执行后,调用测试用例
// async.js
const getData = (cb) => {
setInterval(() => { // 或setTimeout(() => {
cb({name: "mm"})
}, 2000)
}
// async.test.js
it('测试回调函数 获取数据', (done) => {
function cb(data) {
expect(data).toEqual({name: "mm"});
done()
}
getData(cb)
})
Timer Mocks
原生定时器功能依赖于实时时间,对于测试环境来说不太理想。Jest可以将定时器换成允许我们自己控制时间的功能,实现代码同步效果。
// async.js
const getData = (cb) => {
setInterval(() => {
cb({name: "mm"})
}, 2000)
}
// async.test.js
it('测试回调函数 获取数据', (done) => {
function cb(data) {
expect(data).toEqual({name: "mm"});
}
getData(cb)
jest.useFakeTimers() // 启用假定时器
// jest.runAllTimers(); // 运行所有的定时器 对定时器进行mock,适用于setTimeout
// jest.runOnlyPendingTimers(); // 只运行当前等待队列的一个 适用于setInterval
jest.advanceTimersByTime(3); // 快进几秒
})
promise
如果是promise可以采用done 也可以采用async await
// async.js
const getDataByPromise = (cb) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({name: "mm"})
}, 2000)
})
}
// async.test.js
/* it.only('测试promise 获取数据', (done) => {
getDataByPromise().then(data => {
expect(data).toEqual({name: "mm"})
done()
})
}) */
it.only('测试promise 获取数据', async () => {
let data = await getDataByPromise()
expect(data).toEqual({name: "mm"})
})
mock function
当测试 forEach|map等函数的实现时,这类函数为数组中每个项调用回调函数。此时,需要mock function,通过擦去真正的函数实现,捕获函数调用。它允许测试时配置返回值,从而更简单地测试代码之间的链接。
// fn.js
const map = (arr, fn) => {
arr.forEach((item, index) => {
fn(item, index)
});
}
// fn.jest.js
import {map} from '../src/4.fn'
it('测试map方法', () => {
let fn = jest.fn(); //模拟函数,供用户调用,可以记录被执行的过程 ;调用的所有信息,被记录在mock上
map([1,2,3], fn);
expect(fn.mock.calls.length).toBe(3) // fn.mock.calls.length模拟函数被调用次数
expect(fn.mock.calls[0][0]).toBe(1) // 函数第一次调用的第一个参数是1
expect(fn).toHaveBeenCalledTimes(3) // 函数被调用3次
})
coverage覆盖率
coverage,表示测试内容是否覆盖到所有情况。在package.json默认生成的test脚本后添加 jest --coverage
// coverage.js
const flip = (bool) => {
if(bool){
return '正'
} else {
return '反'
// coverage.test.js
it('测试flip方法', () => {
expect(flip(true)).toBe('正')
expect(flip(false)).toBe('反')
})
ajax测试
mock 文件
- 在test文件夹下新建_mocks_/ajax.js
const fetchData = () => {
return new Promise((resolve) => resolve(['mm', 'xx']))
}
export {
fetchData
}
- 在test文件中添加jest.mock,相当于import
// ajax.js
import axios from 'axios'
const fetchData = () => {
return axios.get('/user'); //获取用户数据
}
const sum = (a, b) => {
return a + b
}
// ajax.test.js
jest.mock('./__mocks__/ajax.js') // mock文件
import {fetchData} from '../src/ajax'
let {sum} = jest.requireActual('../src/ajax') // 如果使用真实的函数进行测试,采用requireActual引入
it('测试能否正常获取用户数据',async () => {
let r = await fetchData();
expect(r).toEqual(['mm', 'xx'])
console.log(r)
})
it('测试求和函数sum',async () => {
expect(sum(1, 1)).toBe(2)
})
mock api
mock的api默认会查找__mocks__中对应的axios文件
// __mock__/axios.js
export default {
get(url) {
if(url === '/user') {
return new Promise((resolve) => resolve(['苗苗', '兮兮']))
}
}
}
// ajax.test.js
import {fetchData, sum} from '../src/ajax.js'
it('测试能否正常获取用户数据',async () => {
let r = await fetchData();
expect(r).toEqual(['苗苗', '兮兮'])
})
it('测试求和函数sum',async () => {
expect(sum(1, 1)).toBe(2)
})