Reactjs 如何在Jest和Ezyme中激发和测试真实的粘贴事件(不是通过调用道具模拟的)
我正在尝试对React应用程序中的一个非常简单的功能进行单元测试,通过在事件处理程序中添加Reactjs 如何在Jest和Ezyme中激发和测试真实的粘贴事件(不是通过调用道具模拟的),reactjs,unit-testing,testing,jestjs,enzyme,Reactjs,Unit Testing,Testing,Jestjs,Enzyme,我正在尝试对React应用程序中的一个非常简单的功能进行单元测试,通过在事件处理程序中添加event.preventDefault()阻止用户粘贴到文本区域,如下所示: function handlePaste(event) { event.preventDefault(); } // ... pass it down as props <TextareaComponent onPaste={handlePaste} /> 但是还没有找到一种有效的方法。任何帮助都将不胜
event.preventDefault()
阻止用户粘贴到文本区域,如下所示:
function handlePaste(event) {
event.preventDefault();
}
// ... pass it down as props
<TextareaComponent onPaste={handlePaste} />
但是还没有找到一种有效的方法。任何帮助都将不胜感激 由于您只是针对一个合成事件进行测试(而不是某种辅助操作,比如弹出窗口警告用户粘贴已禁用),因此最简单、正确的解决方案是模拟粘贴事件,向其传递模拟的
preventDefault
函数,然后断言调用了模拟函数
尝试对真实的粘贴事件进行断言是毫无意义的,因为这是React/Javascript实现(例如,断言在触发onPaste
/onChange
事件时调用回调函数)。相反,您需要测试调用回调函数的结果(在本例中,断言调用了event.preventDefault
,如果没有调用,那么我们知道回调函数从未执行过!)
工作示例(单击测试
选项卡运行断言):
为了简单起见,我只是断言输入最初为空,然后仅在触发onChange
事件时更新值。这可以很容易地进行调整,以使某种传入的属性影响默认输入的值
App.js
import React, { useCallback, useState } from "react";
const App = () => {
const [value, setValue] = useState("");
const handleChange = useCallback(
({ target: { value } }) => setValue(value),
[]
);
const handlePaste = useCallback((e) => {
e.preventDefault();
}, []);
const resetValue = useCallback(() => {
setValue("");
}, []);
const handleSubmit = useCallback(
(e) => {
e.preventDefault();
console.log(`Submitted value: ${value}`);
setValue("");
},
[value]
);
return (
<form onSubmit={handleSubmit}>
<label htmlFor="foo">
<input
id="foo"
type="text"
data-testid="test-input"
value={value}
onPaste={handlePaste}
onChange={handleChange}
/>
</label>
<br />
<button data-testid="reset-button" type="button" onClick={resetValue}>
Reset
</button>
<button type="submit">Submit</button>
</form>
);
};
export default App;
import React from "react";
import { configure, mount } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import App from "./App";
configure({ adapter: new Adapter() });
const value = "Hello";
describe("App", () => {
let wrapper;
let inputNode;
beforeEach(() => {
wrapper = mount(<App />);
// finding the input node by a 'data-testid'; this is not required, but easier
// when working with multiple form elements and can be easily removed
// when the app is compiled for production
inputNode = () => wrapper.find("[data-testid='test-input']");
});
it("initially displays an empty input", () => {
expect(inputNode()).toHaveLength(1);
expect(inputNode().props().value).toEqual("");
});
it("updates the input's value", () => {
inputNode().simulate("change", { target: { value } });
expect(inputNode().props().value).toEqual(value);
});
it("prevents the input's value from updating from a paste event", () => {
const mockPreventDefault = jest.fn();
const prefilledText = "Goodbye";
// updating input with prefilled text
inputNode().simulate("change", { target: { value: prefilledText } });
// simulating a paste event with a mocked preventDefault
// the target.value isn't required, but included for illustration purposes
inputNode().simulate("paste", {
preventDefault: mockPreventDefault,
target: { value }
});
// asserting that "event.preventDefault" was called
expect(mockPreventDefault).toHaveBeenCalled();
// asserting that the input's value wasn't changed
expect(inputNode().props().value).toEqual(prefilledText);
});
it("resets the input's value", () => {
inputNode().simulate("change", { target: { value } });
wrapper.find("[data-testid='reset-button']").simulate("click");
expect(inputNode().props().value).toEqual("");
});
it("submits the input's value", () => {
inputNode().simulate("change", { target: { value } });
wrapper.find("form").simulate("submit");
expect(inputNode().props().value).toEqual("");
});
});
import React,{useCallback,useState}来自“React”;
常量应用=()=>{
const[value,setValue]=useState(“”);
const handleChange=useCallback(
({target:{value}})=>setValue(value),
[]
);
const handlePaste=useCallback((e)=>{
e、 预防默认值();
}, []);
const resetValue=useCallback(()=>{
设置值(“”);
}, []);
const handleSubmit=useCallback(
(e) =>{
e、 预防默认值();
log(`submittedvalue:${value}`);
设置值(“”);
},
[价值]
);
返回(
重置
提交
);
};
导出默认应用程序;
App.test.js
import React, { useCallback, useState } from "react";
const App = () => {
const [value, setValue] = useState("");
const handleChange = useCallback(
({ target: { value } }) => setValue(value),
[]
);
const handlePaste = useCallback((e) => {
e.preventDefault();
}, []);
const resetValue = useCallback(() => {
setValue("");
}, []);
const handleSubmit = useCallback(
(e) => {
e.preventDefault();
console.log(`Submitted value: ${value}`);
setValue("");
},
[value]
);
return (
<form onSubmit={handleSubmit}>
<label htmlFor="foo">
<input
id="foo"
type="text"
data-testid="test-input"
value={value}
onPaste={handlePaste}
onChange={handleChange}
/>
</label>
<br />
<button data-testid="reset-button" type="button" onClick={resetValue}>
Reset
</button>
<button type="submit">Submit</button>
</form>
);
};
export default App;
import React from "react";
import { configure, mount } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import App from "./App";
configure({ adapter: new Adapter() });
const value = "Hello";
describe("App", () => {
let wrapper;
let inputNode;
beforeEach(() => {
wrapper = mount(<App />);
// finding the input node by a 'data-testid'; this is not required, but easier
// when working with multiple form elements and can be easily removed
// when the app is compiled for production
inputNode = () => wrapper.find("[data-testid='test-input']");
});
it("initially displays an empty input", () => {
expect(inputNode()).toHaveLength(1);
expect(inputNode().props().value).toEqual("");
});
it("updates the input's value", () => {
inputNode().simulate("change", { target: { value } });
expect(inputNode().props().value).toEqual(value);
});
it("prevents the input's value from updating from a paste event", () => {
const mockPreventDefault = jest.fn();
const prefilledText = "Goodbye";
// updating input with prefilled text
inputNode().simulate("change", { target: { value: prefilledText } });
// simulating a paste event with a mocked preventDefault
// the target.value isn't required, but included for illustration purposes
inputNode().simulate("paste", {
preventDefault: mockPreventDefault,
target: { value }
});
// asserting that "event.preventDefault" was called
expect(mockPreventDefault).toHaveBeenCalled();
// asserting that the input's value wasn't changed
expect(inputNode().props().value).toEqual(prefilledText);
});
it("resets the input's value", () => {
inputNode().simulate("change", { target: { value } });
wrapper.find("[data-testid='reset-button']").simulate("click");
expect(inputNode().props().value).toEqual("");
});
it("submits the input's value", () => {
inputNode().simulate("change", { target: { value } });
wrapper.find("form").simulate("submit");
expect(inputNode().props().value).toEqual("");
});
});
从“React”导入React;
从“酶”导入{configure,mount};
从“酶-适配器-反应-16”导入适配器;
从“/App”导入应用程序;
配置({adapter:newadapter()});
const value=“你好”;
描述(“应用程序”,()=>{
让包装纸;
让输入节点;
在每个之前(()=>{
包装器=mount();
//通过“数据测试ID”查找输入节点;这不是必需的,但更简单
//使用多个表单元素时,可以轻松删除
//当应用程序被编译用于生产时
inputNode=()=>wrapper.find(“[data testid='test-input']”);
});
它(“最初显示一个空输入”,()=>{
expect(inputNode()).toHaveLength(1);
expect(inputNode().props().value).toEqual(“”);
});
它(“更新输入值”,()=>{
inputNode().simulate(“更改”,{target:{value}});
expect(inputNode().props().value).toEqual(value);
});
它(“防止从粘贴事件更新输入值”,()=>{
const mockPreventDefault=jest.fn();
const prefilledText=“再见”;
//使用预填充文本更新输入
inputNode().simulate(“更改”,{target:{value:prefilledText}});
//使用模拟的默认值模拟粘贴事件
//target.value不是必需的,但出于说明目的而包括在内
inputNode().simulate(“粘贴”{
preventDefault:mockPreventDefault,
目标:{value}
});
//断言调用了“event.preventDefault”
expect(mockPreventDefault).toHaveBeenCalled();
//断言输入的值没有更改
expect(inputNode().props().value).toEqual(prefilledText);
});
它(“重置输入值”,()=>{
inputNode().simulate(“更改”,{target:{value}});
查找(“[data testid='reset-button']”)。模拟(“单击”);
expect(inputNode().props().value).toEqual(“”);
});
它(“提交输入值”,()=>{
inputNode().simulate(“更改”,{target:{value}});
包装器。查找(“表单”)。模拟(“提交”);
expect(inputNode().props().value).toEqual(“”);
});
});
这很有道理,感谢您提供了非常全面的示例!