如何使用 React 测试库测试输入?
2022-06-17
19451
我正在尝试通过 React Testing Library 测试
Search
组件的
input
值。
Search 组件接收两个 props:
handleChange
和
input value
:
title
。
我的目标是编写测试,以便最初输入值为空,并且当用户输入某些内容时,测试可以验证监听事件值是否正确,但它始终获取初始值。
我试图在输入之前清除输入,但没有成功。
错误为:
Expected the element to have value:
// indicates as empty string which I expect
Received:
test // it is still the default value: `test`, UserEvent.clear didn't clear
这里有两个测试,首先检查输入最初是否为空,这可以正常工作,第二个测试用于监听输入。
import { fireEvent, screen, waitFor } from "@testing-library/react";
import Search from "../Search";
import { render } from "../../../test-utils/test-utils";
import "@testing-library/jest-dom";
import userEvent from "@testing-library/user-event";
const mockedOnChange = jest.fn();
describe("input should be filled with title", () => {
test("input should be empty initially", async () => {
render(<Search title="" onChangeHadler={mockedOnChange} />);
const input = screen.getByRole("textbox", { name: /search/i });
expect(input).toHaveValue("");
});
// renderComponent wrapps the `Search` component
const renderComponent = (searchInputValue: string) => {
const view = render(
<Search title={searchInputValue} onChangeHadler={mockedOnChange} />
);
return view;
};
test("input should update by typed text", async () => {
renderComponent("test");
const input = await screen.findByRole("textbox", { name: /search/i });
await waitFor(() => expect(input).toHaveValue("test"));
userEvent.clear(input);
expect(input).toHaveValue(""); // error appears here indicating that value was not cleared
// userEvent.type(input, "su");
// await waitFor(() => expect(input).toHaveValue("su"));
});
});
2个回答
您测试的组件是一个受控组件:它从父组件接收(props)其值,并使用函数引用通知父组件用户已更改输入值。通常,此函数将更新父状态,该状态将传递给子组件并用作输入值。
在测试中,您只渲染子组件,并模拟回调函数。因此,您不能指望在测试中触发用户交互时输入值会发生变化。您只能测试
title
props 是否用作输入的值,以及触发用户交互时是否正确调用回调函数(在您的测试中模拟)。但是,如果不渲染父组件,您就无法测试“完整场景”。
test("input value is the title props", async () => {
renderComponent("test");
const input = await screen.findByRole("textbox", { name: /search/i });
await waitFor(() => expect(input).toHaveValue("test"));
});
test("callback function is called on user interactions", async () => {
renderComponent("test");
const input = await screen.findByRole("textbox", { name: /search/i });
userEvent.type(input, "new value");
expect(mockedOnChange).toHaveBeenCalledWith("new value");
});
Florian Motteau
2022-06-17
正如@FlorianMotteau所提到的,如果您想在一系列事件(指针输入、键盘输入等)上测试受控组件(具有获取和更新其值的属性的组件),则应测试控制其值的祖先组件,即控制组件。如果控制组件不存在或过于复杂,您可以创建一个假的组件用于测试目的,如下所示:
import { useState } from 'react';
import { render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { ControlledComponent } from './ControlledComponent'
const ControllingComponent = ({
defaultValue,
mockOnChange,
...rest
} => {
const [value, setValue] = useState(defaultValue);
mockOnChange.mockImplementation(v => setValue(v));
return (
<ControlledComponent value={value} onChange={mockOnChange} {...rest} />
);
};
test('should call onChange on keyboard input', async () => {
const user = userEvent.setup();
const defaultValue = '';
const mockOnChange = jest.fn();
render(
<ControllingComponent
defaultValue={defaultValue}
mockOnChange={mockOnChange}
/>,
);
await user.keyboard('abc');
await waitFor(() => expect(mockOnChange).toHaveBeenCalledTimes(3));
await waitFor(() => expect(mockOnChange).toHaveBeenNthCalledWith(1, 'a'));
await waitFor(() => expect(mockOnChange).toHaveBeenNthCalledWith(2, 'ab'));
await waitFor(() => expect(mockOnChange).toHaveBeenNthCalledWith(3, 'abc'));
});
Géry Ogam
2024-01-19