javascript - fireEvent.click 在进行反应测试时不会对状态进行任何更改
问题描述
我正在尝试使用 react 构建一个简单的计算器应用程序,但在进行一些单元测试时遇到了一些问题。在这里,我附上了我面临一些问题的测试用例。我在网上找到的大多数参考资料都是使用单个组件构建的。在我的情况下,我使用useReducer进行状态管理,使用useContext将数据传递给其他组件,但是在触发click事件时它不会对状态进行任何更改,因此result.textContent
始终保持为空字符串(“”)。任何形式的帮助或提示都将得到应用。
应用程序.test.js
import React from "react";
import { render, fireEvent } from "@testing-library/react";
import App from "./App";
import "@testing-library/jest-dom/extend-expect";
describe("Calculator test", () => {
const { getByTestId } = render(<App />);
const btn7 = getByTestId("nm-btn-7");
const btnSum = getByTestId("nm-btn-sum");
const btn2 = getByTestId("nm-btn-2");
const btnEql = getByTestId("eq-btn");
const result = getByTestId("result");
// expect(btn7.textContent).toBe("7");
it("should return the correct sum value", () => {
fireEvent.click(btn7);
fireEvent.click(btnSum);
fireEvent.click(btn2);
fireEvent.click(btnEql);
console.log(result.textContent);
expect(result.textContent).toBe("9");
});
});
应用程序.tsx
import React from "react";
import styled from "styled-components";
import Calculator from "./components/Calculator";
import "./_app.scss";
function App() {
return (
<Main>
<div>
<Calculator />
</div>
</Main>
);
}
const Main = styled.main`
display: grid;
place-items: center;
min-height: 100vh;
div {
/* width: 25%; */
/* height: 70vh; */
background-color: #061017;
border-radius: 0.5em;
}
`;
export default App;
计算器.tsx
import React from "react";
import styled from "styled-components";
import { useGlobalContext } from "../context/context";
import Buttons from "./Buttons";
import "./_calculator.scss";
const Calculator = () => {
const { newState } = useGlobalContext();
const { inputValue } = newState;
return (
<Container>
<section className="form-section">
<div className="result" data-testid="result">
{inputValue}
</div>
</section>
<section className="button-section">
<Buttons />
</section>
</Container>
);
};
const Container = styled.section`
color: #f2f3f4;
height: inherit;
.form-section {
.result {
width: 100%;
padding: 2em 1em;
font-size: 2em;
height: 5em;
border: none;
outline: none;
border-radius: 0.5em;
background-color: #061017;
color: #f2f3f4;
text-align: right;
}
}
.button-section {
padding: 0.5em;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.5em;
}
`;
export default Calculator;
按钮.tsx
import React, { Fragment } from "react";
import { useGlobalContext } from "../context/context";
import "./_buttons.scss";
const Buttons = () => {
const {
handleNumberOpClick,
handleEqualClick,
handleClearClick,
handleBackspaceClick,
} = useGlobalContext();
return (
<Fragment>
<button className="button-span-2 sp-btn" onClick={handleBackspaceClick}>
C
</button>
<button className="sp-btn" onClick={handleClearClick}>
AC
</button>
{/* <button className="button-blue">+/-</button> */}
<button
className="button-blue nm-btn"
onClick={handleNumberOpClick}
name="/"
>
÷
</button>
<button
className="button-gray"
data-testid="nm-btn-7"
onClick={handleNumberOpClick}
name="7"
>
7
</button>
<button
className="button-gray nm-btn"
onClick={handleNumberOpClick}
name="8"
>
8
</button>
<button
className="button-gray nm-btn"
onClick={handleNumberOpClick}
name="9"
>
9
</button>
<button
className="button-blue nm-btn"
onClick={handleNumberOpClick}
name="*"
>
×
</button>
<button
className="button-gray nm-btn"
onClick={handleNumberOpClick}
name="4"
>
4
</button>
<button
className="button-gray nm-btn"
onClick={handleNumberOpClick}
name="5"
>
5
</button>
<button
className="button-gray nm-btn"
onClick={handleNumberOpClick}
name="6"
>
6
</button>
<button
className="button-blue nm-btn"
onClick={handleNumberOpClick}
name="-"
>
-
</button>
<button
className="button-gray nm-btn"
onClick={handleNumberOpClick}
name="1"
>
1
</button>
<button
className="button-gray"
data-testid="nm-btn-2"
onClick={handleNumberOpClick}
name="2"
>
2
</button>
<button
className="button-gray nm-btn"
onClick={handleNumberOpClick}
name="3"
>
3
</button>
<button
className="button-blue"
data-testid="nm-btn-sum"
onClick={handleNumberOpClick}
name="+"
>
+
</button>
<button
className="button-span-2 button-gray nm-btn"
onClick={handleNumberOpClick}
name="0"
>
0
</button>
<button
className="button-gray nm-btn"
onClick={handleNumberOpClick}
name="."
>
.
</button>
<button
className="nm-btn"
data-testid="eq-btn"
onClick={handleEqualClick}
>
=
</button>
</Fragment>
);
};
export default Buttons;
上下文.tsx
import React, { useContext, useReducer } from "react";
import { reducer } from "../reducer/reducer";
import { IState, IValue } from "../interfaces";
const initialState: IState = {
inputValue: "",
};
const initialContextState: IValue = {
newState: initialState,
handleNumberOpClick: function () {},
handleEqualClick: function () {},
handleClearClick: function () {},
handleBackspaceClick: function () {},
};
const AppContext = React.createContext<IValue>(initialContextState);
const AppProvider = ({ children }: { children: any }) => {
const [newState, dispatch] = useReducer(reducer, initialState);
const handleNumberOpClick = (e: any) => {
e.preventDefault();
let newInputValue = newState.inputValue || "";
if (
newState.inputValue === "Invalid" ||
newState.inputValue === "Infinity"
) {
dispatch({
type: "ON_NUMBER_OP_CLICK",
payload: e.target.name,
});
return;
}
newInputValue = newInputValue.concat(e.target.name);
dispatch({ type: "ON_NUMBER_OP_CLICK", payload: newInputValue });
};
const handleEqualClick = () => {
try {
if (
newState.inputValue === "Invalid" ||
!newState.inputValue ||
newState.inputValue === "Infinity"
) {
dispatch({
type: "ON_EQUAL_CLICK",
payload: "",
});
return;
}
dispatch({
type: "ON_EQUAL_CLICK",
payload: eval(newState.inputValue).toString(),
});
} catch (err) {
dispatch({
type: "ON_EQUAL_CLICK",
payload: "Invalid",
});
}
};
const handleClearClick = () => {
dispatch({ type: "ON_CLEAR_CLICK" });
};
const handleBackspaceClick = () => {
const newInputValue = newState.inputValue.slice(0, -1) || "";
dispatch({ type: "ON_BACKSPACE_CLICK", payload: newInputValue });
};
return (
<AppContext.Provider
value={{
newState,
handleNumberOpClick,
handleEqualClick,
handleClearClick,
handleBackspaceClick,
}}
>
{children}
</AppContext.Provider>
);
};
export const useGlobalContext = () => {
return useContext(AppContext);
};
export { AppContext, AppProvider };
减速器.ts
import { IAction, IState } from "../interfaces";
export const reducer = (currentState: IState, action: IAction): IState => {
const { type, payload } = action;
if (type === "ON_NUMBER_OP_CLICK") {
return { ...currentState, inputValue: payload };
}
if (type === "ON_EQUAL_CLICK") {
return { ...currentState, inputValue: payload };
}
if (type === "ON_CLEAR_CLICK") {
return { ...currentState, inputValue: "" };
}
if (type === "ON_BACKSPACE_CLICK") {
return { ...currentState, inputValue: payload };
}
return currentState;
};
接口.ts
export type IType =
| "ON_NUMBER_OP_CLICK"
| "ON_CLEAR_CLICK"
| "ON_BACKSPACE_CLICK"
| "ON_EQUAL_CLICK";
export interface IState {
inputValue: string;
}
export interface IAction {
type: IType;
payload?: any;
}
export interface IValue {
newState: IState;
handleNumberOpClick: any;
handleEqualClick: any;
handleClearClick: any;
handleBackspaceClick: any;
}
解决方案
您确实在没有 AppProvider 的情况下测试了 App 组件,该组件在索引文件中使用,在 AppComponent 中移动 AppProvider 并且您的测试工作正常。
推荐阅读
- zip - 从 ftp url 导入 zip 文件中的文本 CSV 文件会导致绑定错误 (BoundsError)
- c - 复数 - 如果票数相等,如何打印多个获胜者?
- python - sklearn StandardScaler 似乎无法正常工作
- html - 带边框的选项卡菜单
- sql - 使用 Golang API 将表从 Amazon RDS 导出到 csv 文件
- dafny - Dafny 将多集数据复制到数组中
- python - 我试图了解如何打印数组的所有可能组合
- automation - 从 MQTT 主题或有效负载中读取信息以作为字符串插入?
- php - Pandoc在编织.Rmd文件时导致问题(Raspbian)
- javascript - 有没有更简洁的方法来编写没有一堆 IF 语句?