项目介绍:
1、antd-pro创建的项目
为什么不直接用roadhog test?
看了源码roadhog test不支持–watch–coverage之外的Jest命令,我们的项目需要用到–updateSnapshot等
import test from 'umi-test';
const args = process.argv.slice(2);
const watch = args.indexOf('-w') > -1 || args.indexOf('--watch') > -1;
const coverage = args.indexOf('--coverage') > -1;
test({
watch,
coverage, //不支持其他用户配置的命令
}).catch(e => {
console.log(e);
process.exit(1);
});
文档地址
- Jest相关:
Jest CLI命令查询
全局对象(describe、test等)
expect断言
Jest对象(mock()、spyOn()、fn()等) - Enzyme相关
API
UI测试
- 使用snapshot,使用jest -u更新快照
import renderer from 'react-test-renderer'
// use 'jest -u' to update
test('snapshot of LoginPage', () => {
const rendered = renderer.create(<LoginPage store={store} />).toJSON()
expect(rendered).toMatchSnapshot()
})
- 特别重要的可以使用mount深度渲染
尽量使用shallow前渲染,并且只测试当前组件,子组件应该在子组件的测试文件中测试
import React from 'react'
import { mount } from 'enzyme'
import configureStore from 'redux-mock-store'
import renderer from 'react-test-renderer'
import LoginPage from '../src/routes/User/Login'
const initialProps = {
login: {
status: 'error',
message: '',
},
loading: {
effects: {
'login/login': false,
},
},
}
const mockStore = configureStore()
const store = mockStore(initialProps)
describe('UI render and event', () => {
let loginComponent
let outLayer
let usernameInput
let passwordInput
let submitButton
let errorComponent
beforeEach(() => {
outLayer = mount(<LoginPage store={store} login={{ status: 'xx', message: 'errorMessage' }} />)
loginComponent = outLayer.find('form')
usernameInput = outLayer.find('input#userName')
passwordInput = outLayer.find('input[type="password"]')
submitButton = outLayer.find('button[type="submit"]')
errorComponent = outLayer.find('div.ant-alert-error')
})
afterEach(() => {
outLayer.unmount()
})
it('should render successfully', () => {
expect(outLayer.length).toBe(1)
expect(usernameInput.length).toBe(1)
expect(passwordInput.length).toBe(1)
expect(submitButton.length).toBe(1)
expect(errorComponent.length).toBe(1)
})
})
使用模拟store–redux-mock-store
注意这里的store并不是真正意义上的store,实际上他不会触发action
不推荐使用真正store,如果有必要可以使用createStorestore = createStore(initialProps)
import configureStore from 'redux-mock-store'
const initialProps = {
login: {
status: 'error',
message: '',
},
loading: {
effects: {
'login/login': false,
},
},
}
const mockStore = configureStore()
const store = mockStore(initialProps)
describe('UI render and event', () => {
let outLayer
beforeEach(() => {
outLayer = mount(<LoginPage store={store} />)
})
afterEach(() => {
outLayer.unmount()
})
})
使用simulate模拟事件
test('action triggered by submit', () => {
usernameInput.simulate('change', { target: { value: 'bsc_storage' } })
passwordInput.simulate('change', { target: { value: '12345' } })
loginComponent.simulate('submit')
const actions = store.getActions()
expect(actions[0].type).toBe('login/login')
})
modal测试–effects
使用jest.mock()模拟API数据,使用runSage触发action
mock文件位置为pathTo/api.js则mock文件位置为pathTo/__mocks__
/api.js,并且__mocks__
是大小写敏感的
import { runSaga, effects } from 'redux-saga'
jest.mock('../src/services/api')
test('should effects login successful', async () => {
const dispatched = []
await runSaga({
dispatch: action => dispatched.push(action),
}, modal.effects.login, { payload: payloadRightPassword }, { call, put }).done
const toogleMesageAction = dispatched.filter(action => action.type === 'toggleMessage')[0]
expect(toogleMesageAction).toBeTruthy()
const { token, status, currentUser } = toogleMesageAction.payload.data
expect(token).toBe('mdTiCAVW7SV4cRfK3YPYRTTAhtLpYRMqBmHxznMj')
expect(status).toBe('ok')
expect(currentUser.name).toBe('bsc_storage')
expect(dispatched.filter(action => action.type === 'changeLoginStatus').length).toBe(1)
})
⚠️注意:这里的effetcs不可以当作普通的异步方法进行测试
modal测试-reducers
reducer就是一个普通的函数所以直接测试输出就可以
test('appendValue', () => {
const state = {
status: undefined,
}
const action = {
payload: {
status: 'error',
},
}
const result = modal.reducers.appendValue(state, action)
// toBe({ status: 'error' }) or Object.is() both return false
expect(result).toEqual({ status: 'error' })
})