如何通过选择数据列表来测试更改事件的触发器

问题描述 投票:0回答:2

如何测试用户从数据列表中选择选项的情况?我使用

onChange
在输入上断言
@testing-library/user-event
回调。

这是我的 React 组件:

// MyInput.js

import React from "react";

const MyInput = ({ value, list, id, onChange }) => {
  return (
    <label>
      <strong>{id}</strong>
      <div>
        <input type="text" value={value} onChange={onChange} list={id} />
        <datalist id={id} aria-label="datalist-items">
          {list.map((item) => (
            <option
              key={`item-${item.id}`}
              aria-label="data-list-item"
              value={item.value}
            />
          ))}
        </datalist>
      </div>
    </label>
  );
};

export default MyInput;

这是我失败的测试

// MyInput.spec.js

import React from "react";
import { render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";

import MyInput from "./MyInput";
import data from "./data.json";

describe("MyInput", () => {
  it("should trigger onChange with selected option", () => {
    const onChange = jest.fn();
    const list = [...data.list];
    const screen = render(<MyInput onChange={onChange} list={list} />);

    userEvent.selectOptions(screen.getByLabelText("datalist-items"), "first");

    expect(onChange).toHaveBeenCalledWith("first");
  });
});

提供给组件的数据:

// data.json

{
  "list": [
    { "id": 1, "value": "first" },
    { "id": 2, "value": "second" }
  ]
}

然而,这不起作用。测试报告失败:

expect(jest.fn()).toHaveBeenCalledWith(...expected)

Expected: "first"

Number of calls: 0

  14 |     userEvent.selectOptions(screen.getByLabelText("datalist-items"), "first");
> 16 |     expect(onChange).toHaveBeenCalledWith("first");
  17 |   });
  18 | });

您可以在这个 CodeSandbox 中找到一个实例。

我想模拟现实生活中的场景,其中用户单击输入,呈现数据列表,然后用户单击其中一个选项。我应该以某种方式瞄准渲染的数据列表吗?又如何?

reactjs testing jestjs react-testing-library
2个回答
1
投票

onChange

 事件处理程序由 
<input type='text'>
 jsx 使用,因此您应该使用 
type(element, text, [options])<input>
<textarea>
 内写入文本。 
selectOptions(element, value) 用于选择 <select>
<select multiple>
 元素的指定选项。

MyInput.jsx

import React from 'react'; const MyInput = ({ value, list, id, onChange }) => { return ( <label> <strong>{id}</strong> <div> <input type="text" value={value} onChange={onChange} data-testid="test" list={id} /> <datalist id={id} aria-label="datalist-items"> {list.map((item) => ( <option key={`item-${item.id}`} aria-label="data-list-item" value={item.value} /> ))} </datalist> </div> </label> ); }; export default MyInput;

MyInput.spec.jsx

import React from 'react'; import { render } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import MyInput from './MyInput'; import data from './data.json'; describe('MyInput', () => { it('should trigger onChange with selected option', () => { expect.assertions(6); let events = []; const onChange = jest.fn().mockImplementation((e) => { e.persist(); events.push(e); }); const list = [...data.list]; const screen = render(<MyInput onChange={onChange} list={list} id="test" />); userEvent.type(screen.getByTestId('test'), 'first'); expect(onChange).toBeCalledTimes(5); events.forEach((e) => { expect(onChange).toHaveBeenCalledWith(e); }); }); });
测试结果:

PASS examples/65687415/MyInput.spec.jsx MyInput ✓ should trigger onChange with selected option (44 ms) -------------|---------|----------|---------|---------|------------------- File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s -------------|---------|----------|---------|---------|------------------- All files | 100 | 100 | 100 | 100 | MyInput.jsx | 100 | 100 | 100 | 100 | -------------|---------|----------|---------|---------|------------------- Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 total Time: 5.175 s
封装版本:

"react": "^16.14.0", "@testing-library/react": "^11.2.2", "@testing-library/user-event": "^12.6.0",
    

0
投票
在React/Chrome中,输入和选择会有不同的

nativeEvent

https://stackoverflow.com/a/78015685/5172925

因此,可以通过指定

nativeEvent

 来模拟差异:

fireEvent.change(input, { target: { value: option }, nativeEvent: new Event("input") });
    
© www.soinside.com 2019 - 2024. All rights reserved.