一般组件测试都是用jest,testing-library/react
@testing-library/react : 以用户为中心的方式测试 UI 组件。
@testing-library/jest-dom 好用的自定义 jest 匹配器来测试 DOM 的状态
@testing-library/react-hooks 测试react hook
@testing-library/user-event 测试交互事件
工具:用eslint-plugin-testing-library检测测试代码语法,在 VS Code 中,我们也可以安装插件:Jest Runner。
一般会在src新建一个__test__目录,(以Ashow.js为例)新建一个比如AShow.test.js
平时的前端测试一般都是用这些方法完成前端单元测试
function sum(a, b) {
return a + b;
}
-----
test('测试sum方法:10 + 20 = 30', () => {
expect(sum(10, 10)).toBe(30);
})
const fetchData = () =>
new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ success: true });
}, 2000);
});
-----
test("fetchData 返回结果为{success: true}", () => {
expect(fetchData()).resolves.toMatchObject({
success: true,
});
expect(fetchData()).rejects.toThrow()
});
---或者
test("fetchData 返回结果为{success: true}", async () => {
try {
const data = await fetchData();
expect(data).toEqual({
success: true,
});
} catch (err) {
// 抛出异常
expect(err).toThrow()
}
});
function Ashow({ data }) {
return <h1>{data}</h1>
}
export default Ashow;
test("test-dom-render", () => {
const root = document.createElement("div");
ReactDOM.render(<AShow data={'ashow'}/>, root);
expect(root.querySelector("h1").textContent).toBe("ashow");
})
test('test-snapshoot-json', () => {
render(<AShow
data= {'ashow1'}
></AShow>);
screen.debug();
});
import renderer from 'react-test-renderer';
test('test-snapshoot-json-diff', () => {
const tree = renderer.create(<Ashow
data= {'ashow-1'}
></Ashow>)
.toJSON();
expect(tree).toMatchSnapshot();
}
test('test-click', () => {
const callback = jest.fn();
const wrapper =render(<AShow
data= {'ashow1'}
onClick={callback}
></AShow>);
const el = wrapper.getByText('ashow1')
fireEvent.click(el)
expect(callback).toHaveBeenCalled()
});
---写自定义一个hooks useUndo
import * as React from 'react'
const UNDO = 'UNDO'
const REDO = 'REDO'
const SET = 'SET'
const RESET = 'RESET'
function undoReducer(state, action) {
const {past, present, future} = state
const {type, newPresent} = action
switch (action.type) {
case UNDO: {
if (past.length === 0) return state
const previous = past[past.length - 1]
const newPast = past.slice(0, past.length - 1)
return {
past: newPast,
present: previous,
future: [present, ...future],
}
}
case REDO: {
if (future.length === 0) return state
const next = future[0]
const newFuture = future.slice(1)
return {
past: [...past, present],
present: next,
future: newFuture,
}
}
case SET: {
if (newPresent === present) return state
return {
past: [...past, present],
present: newPresent,
future: [],
}
}
case RESET: {
return {
past: [],
present: newPresent,
future: [],
}
}
default: {
throw new Error(`Unhandled action type: ${type}`)
}
}
}
function useUndo(initialPresent) {
const [state, dispatch] = React.useReducer(undoReducer, {
past: [],
present: initialPresent,
future: [],
})
const canUndo = state.past.length !== 0
const canRedo = state.future.length !== 0
const undo = React.useCallback(() => dispatch({type: UNDO}), [])
const redo = React.useCallback(() => dispatch({type: REDO}), [])
const set = React.useCallback(
newPresent => dispatch({type: SET, newPresent}),
[],
)
const reset = React.useCallback(
newPresent => dispatch({type: RESET, newPresent}),
[],
)
return {...state, set, reset, undo, redo, canUndo, canRedo}
}
export default useUndo
---renderHook编写useUndo测试用例
import {renderHook, act} from '@testing-library/react-hooks'
import useUndo from '../use-undo'
test('allows you to undo and redo', () => {
const {result} = renderHook(() => useUndo('one'))
// assert initial state
expect(result.current.canUndo).toBe(false)
expect(result.current.canRedo).toBe(false)
expect(result.current.past).toEqual([])
expect(result.current.present).toEqual('one')
expect(result.current.future).toEqual([])
// add second value
act(() => {
result.current.set('two')
})
// assert new state
expect(result.current.canUndo).toBe(true)
expect(result.current.canRedo).toBe(false)
expect(result.current.past).toEqual(['one'])
expect(result.current.present).toEqual('two')
expect(result.current.future).toEqual([])
// add third value
act(() => {
result.current.set('three')
})
// assert new state
expect(result.current.canUndo).toBe(true)
expect(result.current.canRedo).toBe(false)
expect(result.current.past).toEqual(['one', 'two'])
expect(result.current.present).toEqual('three')
expect(result.current.future).toEqual([])
// undo
act(() => {
result.current.undo()
})
// assert "undone" state
expect(result.current.canUndo).toBe(true)
expect(result.current.canRedo).toBe(true)
expect(result.current.past).toEqual(['one'])
expect(result.current.present).toEqual('two')
expect(result.current.future).toEqual(['three'])
// undo again
act(() => {
result.current.undo()
})
// assert "double-undone" state
expect(result.current.canUndo).toBe(false)
expect(result.current.canRedo).toBe(true)
expect(result.current.past).toEqual([])
expect(result.current.present).toEqual('one')
expect(result.current.future).toEqual(['two', 'three'])
// redo
act(() => {
result.current.redo()
})
// assert undo + undo + redo state
expect(result.current.canUndo).toBe(true)
expect(result.current.canRedo).toBe(true)
expect(result.current.past).toEqual(['one'])
expect(result.current.present).toEqual('two')
expect(result.current.future).toEqual(['three'])
// add fourth value
act(() => {
result.current.set('four')
})
// assert final state (note the lack of "third")
expect(result.current.canUndo).toBe(true)
expect(result.current.canRedo).toBe(false)
expect(result.current.past).toEqual(['one', 'two'])
expect(result.current.present).toEqual('four')
expect(result.current.future).toEqual([])
})
toBeDisabled
toBeEnabled
toBeEmpty
toBeEmptyDOMElement
toBeInTheDocument:和toBeNull检查元素是否存在(和)
toBeInvalid
toBeRequired
toBeValid
toBeVisible
toContainElement
toContainHTML
toHaveAttribute
toHaveClass
toHaveFocus
toHaveFormValues
toHaveStyle
toHaveTextContent
toHaveValue
toHaveDisplayValue
toBeChecked
toBePartiallyChecked
toHaveDescription
getByText: ,判断是否存在getByText(‘Add’)
getByRole:函数通常用于通过aria-label属性检索元素