【发布时间】:2021-11-04 23:04:39
【问题描述】:
我正在尝试使用 react 构建一个简单的计算器应用程序,但在进行一些单元测试时遇到了一些问题。在这里,我附上了我面临一些问题的测试用例。我在网上找到的大多数参考资料都是使用单个组件构建的。在我的情况下,我使用 useReducer 进行状态管理,使用 useContext 将数据传递给其他组件,但是在触发 click 事件时它不会t 对状态进行任何更改,因此result.textContent 始终保持为空字符串(“”)。任何形式的帮助或提示都将得到应用。
App.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");
});
});
App.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;
Calculator.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;
Buttons.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;
context.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 };
reducer.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;
};
interfaces.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;
}
【问题讨论】:
标签: javascript reactjs unit-testing