React+Jest+Enzyme測試
阿新 • • 發佈:2018-12-09
專案介紹:
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' })
})