希望有人能指出我正确的方向。基本上我已经创建了一个使用钩子的反应应用程序,特别是 useContext、useEffect 和 useReducer。我的问题是我似乎无法进行测试来检测相关组件的点击或调度事件。
可以在以下位置找到我的应用程序的精简版本:https ://github.com/atlantisstorm/hooks-testing 测试与 layout.test.js 脚本相关。
我尝试了各种方法、模拟调度、useContext 等的不同方式,但对此并不满意。最新版本。
layout.test.js
import React from 'react';
import { render, fireEvent } from "@testing-library/react";
import Layout from './layout';
import App from './app';
import { Provider, initialState } from './context';
const dispatch = jest.fn();
const spy = jest
.spyOn(React, 'useContext')
.mockImplementation(() => ({
state: initialState,
dispatch: dispatch
}));
describe('Layout component', () => {
it('starts with a count of 0', () => {
const { getByTestId } = render(
<App>
<Provider>
<Layout />
</Provider>
</App>
);
expect(dispatch).toHaveBeenCalledTimes(1);
const refreshButton = getByTestId('fetch-button');
fireEvent.click(refreshButton);
expect(dispatch).toHaveBeenCalledTimes(3);
});
});
布局.jsx
import React, { useContext, useEffect } from 'react';
import { Context } from "./context";
const Layout = () => {
const { state, dispatch } = useContext(Context);
const { displayThings, things } = state;
const onClickDisplay = (event) => {
// eslint-disable-next-line
event.preventDefault;
dispatch({ type: "DISPLAY_THINGS" });
};
useEffect(() => {
dispatch({ type: "FETCH_THINGS" });
}, [displayThings]);
const btnText = displayThings ? "hide things" : "display things";
return (
<div>
<button data-testid="fetch-button" onClick={onClickDisplay}>{btnText}</button>
{ displayThings ?
<p>We got some things!</p>
:
<p>No things to show!</p>
}
{ displayThings && things.map((thing) =>
<p>{ thing }</p>
)}
</div>
)
}
export default Layout;
应用程序.jsx
import React from 'react';
import Provider from "./context";
import Layout from './layout';
const App = () => {
return (
<Provider>
<Layout />
</Provider>
)
}
export default App;
上下文.jsx
import React, { createContext, useReducer } from "react";
import { reducer } from "./reducer";
export const Context = createContext();
export const initialState = {
displayThings: false,
things: []
};
export const Provider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<Context.Provider value={{ state, dispatch }}>
{children}
</Context.Provider>
);
};
export default Provider;
减速器.jsx
export const reducer = (state, action) => {
switch (action.type) {
case "DISPLAY_THINGS": {
const displayThings = state.displayThings ? false : true;
return { ...state, displayThings };
}
case "FETCH_THINGS": {
const things = state.displayThings ? [
"thing one",
"thing two"
] : [];
return { ...state, things };
}
default: {
return state;
}
}
};
我确信当我看到它时答案会很容易,但只是想弄清楚我可以检测到点击事件并检测到“调度”事件吗?(我已经在主应用程序中进行了单独的测试,以正确测试调度响应/操作)
先感谢您。
编辑 好吧,我想我有一个合理但不完美的解决方案。首先,我刚刚向 context.jsx 模块添加了可选的 testDispatch 和 testState 道具。
新上下文.jsx
import React, { createContext, useReducer } from "react";
import { reducer } from "./reducer";
export const Context = createContext();
export const initialState = {
displayThings: false,
things: []
};
export const Provider = ({ children, testDispatch, testState }) => {
const [iState, iDispatch] = useReducer(reducer, initialState);
const dispatch = testDispatch ? testDispatch : iDispatch;
const state = testState ? testState : iState;
return (
<Context.Provider value={{ state, dispatch }}>
{children}
</Context.Provider>
);
};
export default Provider;
然后在 layout.test.jsx 中,我只是简单地传入模拟的 jest 调度函数和必要的状态。还移除了外部 App 包装,因为这似乎可以防止道具通过。
新的 layout.test.jsx
import React from 'react';
import { render, fireEvent } from "@testing-library/react";
import Layout from './layout';
import { Provider } from './context';
describe('Layout component', () => {
it('starts with a count of 0', () => {
const dispatch = jest.fn();
const state = {
displayThings: false,
things: []
};
const { getByTestId } = render(
<Provider testDispatch={dispatch} testState={state}>
<Layout />
</Provider>
);
expect(dispatch).toHaveBeenCalledTimes(1);
expect(dispatch).toHaveBeenNthCalledWith(1, { type: "FETCH_THINGS" });
const refreshButton = getByTestId('fetch-things-button');
fireEvent.click(refreshButton);
expect(dispatch).toHaveBeenCalledTimes(2);
// Important: The order of the calls should be this, but dispatch is reporting them
// the opposite way around in the this test, i.e. FETCH_THINGS, then DISPLAY_THINGS...
//expect(dispatch).toHaveBeenNthCalledWith(1, { type: "DISPLAY_THINGS" });
//expect(dispatch).toHaveBeenNthCalledWith(2, { type: "FETCH_THINGS" });
// ... so as dispatch behaves correctly outside of testing for the moment I'm just settling for
// knowing that dispatch was at least called twice with the correct parameters.
expect(dispatch).toHaveBeenCalledWith({ type: "DISPLAY_THINGS" });
expect(dispatch).toHaveBeenCalledWith({ type: "FETCH_THINGS" });
});
});
但是,如上所述,有一点需要注意,当“获取事物按钮”被触发时,它会以错误的顺序报告调度。:/所以我只是决定知道触发的正确呼叫,但如果有人知道为什么呼叫顺序不符合预期,我会很高兴知道。
https://github.com/atlantisstorm/hooks-testing更新以反映上述内容,如果有人感兴趣的话。