在 Jest 中测试 onChange 函数
Posted
技术标签:
【中文标题】在 Jest 中测试 onChange 函数【英文标题】:Testing onChange function in Jest 【发布时间】:2018-06-19 05:09:13 【问题描述】:我对 Jest 和一般测试比较陌生。我有一个带有输入元素的组件:
import * as React from "react";
export interface inputProps
placeholder: string;
className: string;
value: string;
onSearch: (depID: string) => void;
onSearch(event: any)
event.preventDefault();
//the actual onclick event is in another Component
this.props.onSearch(event.target.value.trim());
export class InputBox extends React.Component<inputProps, searchState>
render()
return (
<input
onChange=this.onSearch //need to test this
className=this.props.className
type="text"
value=this.props.value
placeholder=this.props.placeholder />
);
我想要一个测试来检查输入元素的onChange
是一个以输入元素的value
属性作为参数的函数。这是我到目前为止所取得的成就:
//test to see the input element's onchange
//returns a function that takes its value as a param
it("onChange param is the same value as the input value", () =>
const mockFn = jest.fn();
const input = enzyme.shallow(<InputBox
value="TestVal"
placeholder=""
className=""
onSearch=mockFn/>);
input.find('input').simulate('change', preventDefault() );
expect(mockFn.mock.calls).toBe("TestVal");
);
我要离开这里的第一个解决方案Simulate a button click in Jest 还有:https://facebook.github.io/jest/docs/en/mock-functions.html
编辑:运行上述代码会引发以下错误:
TypeError: Cannot read property 'value' of undefined
【问题讨论】:
【参考方案1】:我为此苦苦挣扎了好几个小时。另外,因为我在一页上有多个选择字段。我发现 Textfield 解决方案的工作方式与文档上给出的 Select.test 不同。
在代码中我用 id 定义了 SelectProps。 (您也可以使用 data-testid)
我只能通过单击此字段来触发下拉菜单。
<TextField
select
variant = "outlined"
value = input.value || Number(0)
onChange = value => input.onChange(value)
error = Boolean(meta.touched && meta.error)
open = open
SelectProps =
id: `$input.name-select`,
MenuProps:
anchorOrigin:
vertical: "bottom",
horizontal: "left"
,
transformOrigin:
vertical: "top",
horizontal: "left"
,
getContentAnchorEl: null
...props >
//yourOptions Goes here
</TextField>
在我的测试中。
const pickUpAddress = document.getElementById("address-select");
UserEvent.click(pickUpAddress);
UserEvent.click(screen.getByTestId("address-select-option-0"));
之后的工作就像一个魅力。希望这会有所帮助。
【讨论】:
【参考方案2】:这个怎么样?我使用酶模拟更改事件并执行快照测试。 组件
import React, FunctionComponent, useState from 'react';
const Index: FunctionComponent = () =>
const [val, setVal] = useState('');
const onInputChange = e =>
e.preventDefault();
setVal(e.target.value);
;
return (
<input type='text' onChange=onInputChange value=val />
);
;
export default Index;
单元测试
describe('Index with enzyme', () =>
it('Should set value to state when input is changed', () =>
const container = shallow(<Index />);
const input = container.find('input');
input.simulate('change', preventDefault: jest.fn, target: value: "foo" );
expect(container).toMatchSnapshot();
);
);
快照
exports[`Index with enzyme Should set value to state when input is changed 1`] = `
<input
onChange=[Function]
type="text"
value="foo"
/>
`;
【讨论】:
【参考方案3】:我认为你的代码 sn-p 的语法应该是:
import React from 'react';
export default class InputBox extends React.Component
onSearch(event)
event.preventDefault();
this.props.onSearch(event.target.value.trim());
render () return (<input onChange=this.onSearch.bind(this) />);
测试失败是因为,正如您在事件对象上定义 preventDefault
函数一样,您还必须定义在 onSearch
函数上使用的其他属性。
it('should call onChange prop', () =>
const onSearchMock = jest.fn();
const event =
preventDefault() ,
target: value: 'the-value'
;
const component = enzyme.shallow(<InputBox onSearch=onSearchMock />);
component.find('input').simulate('change', event);
expect(onSearchMock).toBeCalledWith('the-value');
);
之前的测试代码需要定义事件形状,因为您使用的是浅层渲染。如果您想测试您的onSearch
函数是否使用了实际输入值,您需要尝试使用enzyme.mount
进行完整渲染:
it('should call onChange prop with input value', () =>
const onSearchMock = jest.fn();
const component = enzyme.mount(<InputBox onSearch=onSearchMock value="custom value" />);
component.find('input').simulate('change');
expect(onSearchMock).toBeCalledWith('custom value');
);
【讨论】:
不客气! @ZeroDarkThirty 也许你可以通过这个react-seed 项目学到更多东西。 谢谢!顺便说一句,在这种情况下使用mount
而不是shallow
有什么意义?
在这种特殊情况下,主要区别在于挂载的组件会为您创建事件,允许在调用 prop 函数回调 (onSearch
) 时测试使用的实际输入值。通常,shallow
渲染用于真正的单元测试,因为没有子组件被渲染。而mount
render 用于完整的 DOM 渲染,当您想要测试完整的组件生命周期时,这是理想的选择......检查完整渲染 API here【参考方案4】:
对于那些使用 TypeScript(并借鉴上面的答案)进行测试的人,您需要执行类型强制 (as React.ChangeEvent<HTMLInputElement>
) 以确保 linter 可以将签名视为兼容:
反应文件
export class InputBox extends React.Component<inputProps, searchState>
onSearch(event: React.ChangeEvent<HTMLInputElement>)
event.preventDefault();
//the actual onclick event is in another Component
this.props.onSearch(event.target.value.trim());
render()
return (
<input
onChange=this.onSearch //need to test this
className=this.props.className
type="text"
value=this.props.value
placeholder=this.props.placeholder />
);
测试文件
it('should call onChange prop', () =>
const onSearchMock = jest.fn();
const event =
target: value: 'the-value'
as React.ChangeEvent<HTMLInputElement>;
const component = enzyme.shallow(<InputBox onSearch=onSearchMock />);
component.find('input').simulate('change', event);
expect(onSearchMock).toBeCalledWith('the-value');
);
或者
it('should call onChange prop', () =>
const onSearchMock = jest.fn();
const event =
target: value: 'the-value'
as React.ChangeEvent<HTMLInputElement>;
const component = enzyme.mount<InputBox>(<InputBox onSearch=onSearchMock />);
const instance = component.instance();
instance.onSearch(event);
expect(onSearchMock).toBeCalledWith('the-value');
);
【讨论】:
【参考方案5】:我想出了解决办法。
因此,我们必须在simulate
的第二个参数中传递它,而不是传递InputBox
中的值,如下所示。然后我们只需检查第一次调用mockFn
的第一个参数是否相等。另外,我们可以去掉event.preventDefault()
;
it("onChange param is the same value as the input element's value property", () =>
const mockFn = jest.fn();
const input = enzyme.shallow(<InputBox
value=""
placeholder=""
className=""
onSearch=mockFn/>);
input.find('input').simulate('change', target: value: 'matched' );
expect(mockFn.mock.calls[0][0]).toBe('matched');
);
【讨论】:
以上是关于在 Jest 中测试 onChange 函数的主要内容,如果未能解决你的问题,请参考以下文章
React 测试库未在快照中显示输入 onChange 道具
使用 Jest 在 Nodejs 中测试 Azure 函数 - process.env 变量显示未定义
快照测试的工作原理以及 toMatchSnapshot() 函数在 React 组件的 Jest 快照测试中的作用是啥?