React测试:fireEvent.change修改子组件InputBox值失败问题
问题:React测试中无法修改外部InputBox组件的值
我有一个主组件,代码如下:
import React, {useEffect, useState} from 'react' import InputBox from "@someOther/forms/lib/components/input-box"; export const mainComponent = ():JSX.Element => { const [value, setValue] = useState({number:{value:""}}); const handleUpdate = (fieldValue, val) => { setValue({number: {value: val}}) } const {number} = value; return( <h1>Welcome!</h1> <div> <InputBox type="text" labelTedxt="" id="num" value={number.value} onUpdate={(value:string) => handleUpdate("number", value)} /> </div> ) } export default mainComponent;
我尝试在测试中使用fireEvent.change修改这个引入自外部的InputBox组件的输入值,但测试失败。测试文件代码如下:
import {mainComponent} from "./mainComponent"; import {screen, render, fireEvent} from "@testing-library/react"; import InputBox from "@someOther/forms/lib/components/input-box"; describe("Verify if input box accepts value", () => { const {container} = render(<mainComponent/>); it("should change input element value", () => { const inputEl = screen.getAllByRole("textbox")[0]; fireEvent.change(inputEl, {target: {value: "fakeValue214353"}}); const inputElAfterValueChange = screen.getAllByRole("textbox")[0]; expect(inputElAfterValueChange.getAttribute("value")).to.equal(fakeValue214353); }); });
运行测试后失败,通过screen.debug()查看发现输入元素仍显示<input value="" ..../>。我希望解决该问题,或者能在render时给InputBox传值,亦或是在测试中调用handleUpdate方法。
解决方案
1. 修复测试代码的语法错误
你的测试代码中,expect断言里的fakeValue214353是未定义变量,必须改为字符串字面量:
expect(inputElAfterValueChange.getAttribute("value")).to.equal("fakeValue214353");
2. 解决fireEvent.change无效的问题
外部组件InputBox可能没有绑定原生onChange事件,而是使用自定义的onUpdate回调。直接触发原生change事件不会触发onUpdate,可以尝试两种方法:
方法A:使用userEvent模拟真实用户输入
userEvent更贴近真实用户交互,能触发组件内部的自定义事件逻辑:
import userEvent from '@testing-library/user-event'; it("should change input element value", async () => { const user = userEvent.setup(); const inputEl = screen.getAllByRole("textbox")[0]; await user.type(inputEl, "fakeValue214353"); // 推荐使用toHaveValue断言,而非getAttribute expect(inputEl).toHaveValue("fakeValue214353"); });
方法B:直接触发InputBox的onUpdate回调
如果知道InputBox的内部结构,可以直接获取组件实例并调用onUpdate:
it("should change input element value", () => { // 找到InputBox组件实例(假设InputBox会转发id为testId) const inputBox = screen.getByTestId("num"); inputBox.props.onUpdate("fakeValue214353"); const inputEl = screen.getAllByRole("textbox")[0]; expect(inputEl).toHaveValue("fakeValue214353"); });
3. 渲染时给InputBox传初始值
修改主组件,支持接收初始值props,这样测试时可以直接传入预设值:
// 修改主组件 export const mainComponent = ({ initialValue = { number: { value: "" } } }): JSX.Element => { const [value, setValue] = useState(initialValue); const handleUpdate = (fieldValue, val) => { setValue({ number: { value: val } }); }; const { number } = value; return ( <h1>Welcome!</h1> <div> <InputBox type="text" labelTedxt="" id="num" value={number.value} onUpdate={(value: string) => handleUpdate("number", value)} /> </div> ); };
测试时传入初始值:
it("should render with initial value", () => { render(<mainComponent initialValue={{ number: { value: "fakeValue214353" } }} />); const inputEl = screen.getAllByRole("textbox")[0]; expect(inputEl).toHaveValue("fakeValue214353"); });
4. 测试中直接调用handleUpdate方法
可以通过ref暴露组件内部的handleUpdate方法,方便测试调用:
// 修改主组件,用ref暴露handleUpdate import React, { useState, useRef } from 'react'; export const mainComponent = (): JSX.Element => { const [value, setValue] = useState({ number: { value: "" } }); const handleUpdateRef = useRef<(fieldValue: string, val: string) => void>(); handleUpdateRef.current = (fieldValue, val) => { setValue({ number: { value: val } }); }; const { number } = value; return ( <div ref={handleUpdateRef}> <h1>Welcome!</h1> <div> <InputBox type="text" labelTedxt="" id="num" value={number.value} onUpdate={(value: string) => handleUpdateRef.current?.("number", value)} /> </div> </div> ); };
测试时获取ref并调用方法:
it("should update value via handleUpdate", () => { const { container } = render(<mainComponent />); const handleUpdate = (container.firstChild as any).current; handleUpdate("number", "fakeValue214353"); const inputEl = screen.getAllByRole("textbox")[0]; expect(inputEl).toHaveValue("fakeValue214353"); });
内容的提问来源于stack exchange,提问作者krunal
相关产品推荐
相关产品推荐

