[英]Testing setState anonymous function call
考虑 JSX 中的以下按钮:
interface Props {
index: number;
setIndex: Dispatch<SetStateAction<number>>;
}
export function Button({ index, setIndex }: Props) {
return (
<button
type="button"
data-testid="prev_btn"
onClick={() => setIndex(prevIndex => prevIndex - 1)}
>
Previous
</button>
);
}
我想在 Jest / React 测试库中编写一个测试来呈现按钮并将setIndex
作为道具传递,然后检查是否使用正确的值调用了setIndex
。 例如:
const mockSetIndex = jest.fn();
const mockProps = {
setIndex: mockSetIndex,
index: 1,
};
describe('Button test', () => {
it('should update the index when the "previous" button is clicked', () => {
const { getByTestId } = render(<Button {...mockProps} />);
const prevBtn = getByTestId('prev_btn');
userEvent.click(prevBtn);
expect(mockSetIndex).toBeCalledWith(0);
});
});
我得到的结果是失败的:
expect(jest.fn()).toBeCalledWith(...expected)
Expected: 0
Received: [Function anonymous]
还有另一个增加索引的类似按钮的实例,所以我想测试这两个按钮的行为是否正确。
您使用setIndex
而不是数字调用 setIndex。 虽然这是setIndex
可接受的论点,但它不是您要测试的内容。 将您的 function 更改为类似
interface Props {
index: number;
setIndex: Dispatch<SetStateAction<number>>;
}
export function Button({ index, setIndex }: Props) {
return (
<button
type="button"
data-testid="prev_btn"
onClick={() => setIndex(index - 1)}
>
Previous
</button>
);
}
或者测试它是否传递了具有预期行为的 function
describe('Button test', () => {
it('should update the index when the "previous" button is clicked', () => {
const { getByTestId } = render(<Button {...mockProps} />);
const prevBtn = getByTestId('prev_btn');
userEvent.click(prevBtn);
const passedFunction = mockProps.setIndex.mock.calls[0][0]
expect(passedFunction(1)).toEqual(0);
});
});
根据Button
的属性,我猜它的用法看起来像这样:
const Parent = () => {
const [index, setIndex] = useState(1);
return (
<>
<Button index={index} setIndex={setIndex} />
<p data-testid="current_index">Current index: {index}</p>
</>
);
};
这使Parent
无法控制其自己的 state, Button
可以调用例如setIndex(100)
,无论它是否具有任何实际意义。 由于这个原因,将 setter 直接传递给另一个组件通常是一种代码味道。
一个不太紧密耦合的结构,其中Parent
负责它自己的 state,看起来像这样:
const Parent = () => {
const [index, setIndex] = useState(1);
const decrement = () => setIndex((previousIndex) => previousIndex - 1);
return (
<>
<p>Current index: {index}</p>
<Button onClick={decrement} />
</>
);
};
这更容易测试:
describe("Button", () => {
it("should signal when the Previous button is clicked", () => {
const onClick = jest.fn();
render(<Button onClick={onClick} />);
userEvent.click(screen.getByRole("button", { name: /previous/i }));
expect(onClick).toHaveBeenCalled();
});
});
(请注意,我正在遵循优先级指南,尽可能多地使用面向用户的、可访问的选择器。)
或者,如果Button
仅在Parent
中使用(如 state 上的高耦合级别所建议的那样),请在该上下文中对其进行测试:
describe("Parent", () => {
it("should allow the previous thing to be selected", () => {
render(<Parent />);
userEvent.click(screen.getByRole("button", { name: /previous/i }));
// assert on actual outcome of index changing, e.g.
expect(screen.getByTestId("current_index")).toHaveTextContent("Current index: 0");
});
});
在这种情况下,在Button
与其Parent
之间的边界进行测试并不合适。 要指示这种耦合,您可以将Button
移动到定义Parent
的模块中,而不是将其导出以供单独访问 - 将按钮已提取到单独组件的事实视为实现细节。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.