当产品变得更大更复杂
导致模块间依赖增加
import test from 'ava'
test(t => {
t.is(1 + 1, 2)
})
yarn add ava
ava --watch
一段代码(方法,一系列操作)的返回值是否符合我们的预期
Redux就是用一堆Reducer函数来reduce所有事件用来做全局Store的状态机(FSM)
Like VUEX
export const engineerManage = (state = [], action) => {
switch (action.type) {
case 'engineer/ADD':
return [...state, action.engineer]
case 'engineer/DEL':
return _.drop(state, action.engineer);
default: return state
}
}
工程师管理系统有增加和删除工程师两个功能
test('parking lot', t => {
const initial = engineerManage(undefined, {})
t.deepEqual(initial, [], 'should be empty when init')
const add = engineerManage(initial, { type: 'engineer/ADD', engineer: '潘嘉慧' })
t.deepEqual(add, ['潘嘉慧'], 'should add engineer')
const del = engineerManage(add, { type: '赵楠' })
t.deepEqual(del, ['潘嘉慧'], 'shouldn't delete any engineer')
})
Model层的测试非常简单和机械化
简单到我们可以自动生成
(redux-devtools写完实现,在浏览器里打开,反过来还可以自动生成各种框架的测试代码,粘贴回来就行了)
渲染和捕捉事件
渲染和以Virtual DOM 的模式封装了恶心的浏览器基础设置,使得我们可以以函数和数据结构描述组件
const Greeter = ({ name }) =>
<p>你好 {name}!</p>
render(
<Greeter name="React"/>,
document.body
)
import { renderJSX, JSX } from 'jsx-test-helpers'
const Paragraph = ({ children }) => <p>{children}</p>
const Greeter = ({ name }) => <Paragraph>你好 {name}!</Paragraph>
test('Greeter', t => {
t.is(renderJSX(<Greeter name="皓哥"/>),
JSX(<Paragraph>你好 皓哥!</Paragraph>),
'should render greeting text with name')
})
jsx-test-helpers
针对View事件的测试
const TextField = ({ label, onChange }) => <label>
{label}
<input type="text" onChange={onChange} />
</label>
test('TextField', t => {
const onChange = () => {}
const actual = renderJSX(<TextField label="Email" onChange={onChange} />)
const expected = JSX(<label>
Email
<input type="text" onChange={onChange}/>
</label>)
t.is(actual, expected)
})
一个简单的输入框组件
注入按钮点击事件
import { takeEvery, put, call, fork, cancel } from 'redux-saga/effects'
function *account() {
yield call(takeEvery, 'login/REQUESTED', login)
}
function *login({ name, password }) {
try {
const { token } = yield call(fetch, '/login', {
method: 'POST',
body: { name, password }
})
yield put({ type: 'login/SUCCEEDED', token })
} catch (error) {
yield put ({ type: 'login/FAILED', error })
}
}
test('account saga', t => {
const gen = account()
t.deepEqual(gen.next().value, call(takeEvery, 'login/REQUESTED', login))
})
test('login saga', t => {
const gen = login({ name: 'John', password: 'super-secret-123'})
const request = gen.next().value
t.deepEqual(request, call(fetch, '/login', {
method: 'POST',
body: { name: 'John', password: 'super-secret-123'}
}))
const response = gen.next({ token: 'non-human-readable-token' }).value
t.deepEqual(response, put({
type: 'login/SUCCEEDED',
token: 'non-human-readable-token'
}))
const failure = gen.throw('You code just exploded!').value
t.deepEqual(failure, put({
type: 'login/FAILED',
error: 'You code just exploded!'
}))
})
[React全家桶与前端单元测试艺术] https://mp.weixin.qq.com/s/1V6x2_zEisceJcmbAvjoPA