前端单元测试和e2e测试
一、单元测试
1.什么是单元测试?
单元测试就是对代码中的最小单元进行测试,
前端代码中的最小单元是什么?
一个组件,一个按钮,一个函数,前端的组件太多了,所以没有必要对所有的单元都做测试。
2.可以测些什么?
测没有I/O和UI依赖的工具函数,入参出参是否正确,
测UI 表现
测试异步(异步请求回调)
3.测试框架、测试库
1.测试框架 jest
2.测试库 enzyme
React的JavaScript测试工具,主要用于单元测试中 渲染和操作dom
https://enzymejs.github.io/enzyme/
二、e2e测试
1.什么是e2e测试?
end to end 端对端,也叫冒烟测试,
在真实浏览器环境下,模拟用户的行为,也就是代替人工操作应用,去测试前端应用的流程。
2.可以测些什么?
测整个页面的流程和表现
测试需求是否正确完成
模拟用户操作后,得到的是不是预期结果
3.测试框架、测试工具
1.测试框架 jest
2.测试工具 puppeteer
使用 chrome / chromium 作为浏览器环境运行应用,并且提供了非常语义化的 API 来描述业务逻辑
https://github.com/googlechrome/puppeteer
三、测试框架 jest
Facebook 的一套开源的 JavaScript 测试框架 ,集成了断言、JSDom、测试覆盖率报告功能。
单元测试 例:
1.测试dva reducers
测试reducers 中 changeStateValues
changeStateValues
changeStateValues(state, { payload }) {
return {
...state,
...payload,
};
},
测试changeStateValues
import ContractManageModel from '@/pages/Personnel/ContractManage/model';
describe('test reducers', () => {
const initState = {};
it('changeStateValues', () => {
const changeStateValues = ContractManageModel.reducers.changeStateValues;
const payload = {
testState: true,
};
const state = changeStateValues(initState, { payload });
expect(state).toEqual({ ...payload });
});
});
2.测试dva effects
测试effects 中 fetchList
fetchList
*fetchList({ payload }, { call, put }) {
yield put({
type: 'changeLoading',
payload: true,
});
const res = yield call(getContractList, payload);
if (res.code == 200) {
yield put({
type: 'changeStateValues',
payload: {
list: formatListData(_.get(res, 'data.list', [])),
},
});
yield put({
type: 'changePagination',
payload: { total: _.get(res, 'data.allRows', 0) },
});
}
yield put({
type: 'changeLoading',
payload: false,
});
return res;
},
effect函数是个generator函数,generator中yield会暂停函数,需要手动next(),
dva中redux-saga 用来管理副作用,处理了generator函数继续执行,
测试时,就需要手动next了,
测试fetchList
describe('test effect', () => {
it('fetchList', () => {
const actionPayload = {
payload: {
currentPage: 1,
pageRows: 10,
},
};
const fetchList = ContractManageModel.effects.fetchList(actionPayload, { call, put });
let next = fetchList.next();
// put changeLoading
expect(next.value).toEqual(
put({
type: 'changeLoading',
payload: true,
})
);
next = fetchList.next();
// call
expect(next.value).toEqual(call(getContractList, actionPayload.payload));
// 模拟 res
const res = {
code: 200,
data: { list: [], allRows: 0 },
};
next = fetchList.next(res);
// put changeStateValues
// 模拟formatListData
const formatListDataMock = jest.fn().mockReturnValue(res.data.list);
expect(next.value).toEqual(
put({
type: 'changeStateValues',
payload: {
list: formatListDataMock(),
},
})
);
next = fetchList.next();
// put changePagination
expect(next.value).toEqual(
put({
type: 'changePagination',
payload: {
total: res.data.allRows,
},
})
);
next = fetchList.next();
// put changeLoading
expect(next.value).toEqual(
put({
type: 'changeLoading',
payload: false,
})
);
const result = fetchList.next(res);
expect(result.done).toEqual(true);
expect(result.value).toEqual(res);
});
});
3.测组件
浅渲染 dva包裹的组件
快照测试 npm i -D enzyme-to-json
describe('合同编辑快照测试', () => {
const tree = shallow(<ContractEdit.WrappedComponent />);
it('匹配快照', () => {
expect(toJson(tree)).toMatchSnapshot();
});
});
npm run test 后会生成一个快照文件,如果后面修改了dom,再跑测试会告诉哪里变动了,
完整渲染mount dva form 包裹的组件
import configureStore from 'redux-mock-store';
const mockStore = configureStore([]);
describe('合同编辑快照测试', () => {
let store;
let component;
beforeEach(() => {
store = mockStore({
global: {},
});
const mockFn = jest.fn();
const testForm = {
getFieldDecorator: jest.fn(opts => c => c),
getFieldValue: jest.fn(opts => c => c),
validateFields: jest.fn(opts => c => c),
};
component = mount(<Mycomponent contractManage={{ stepFormValues: {} }} store={store} dispatch={mockFn} form={testForm} />);
});
it('匹配快照', () => {
expect(toJson(component)).toMatchSnapshot();
});
});
静态渲染render 只是把render中jsx渲染成html
异步测试
const handleStopPromise = handleStop => {
return new Promise((resolve, reject) => {
if (handleStop) {
resolve(handleStop());
}
});
};
describe('测试异步', () => {
it('handleStopPromise', () => {
const mockFn = jest.fn().mockReturnValue(true);
expect(handleStopPromise(mockFn)).resolves.toEqual(true);
});
});