当产品变得更大更复杂
导致模块间依赖增加
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