reactjs - 如何测试在功能组件中定义的方法,该方法与 DOM 元素交互并且没有参数
问题描述
我一直无法在我的一个按钮(一个 React 功能组件)上获得 100% 的测试覆盖率。基本上,当它被单击时,它会执行一些代码,然后还会从这个onClick
名为resetButtons
. 此方法将在应用程序中找到所有类似的按钮并删除一个类。这是一种先发制人的行为,因此一次只能有一个按钮active
。
到目前为止,我已经测试了点击使用.simulate
,传入了一个模拟的domElement
. 然后测试该domElement.classList.add
方法是否被调用'active'
。
显然这是一个以 DOM 为中心的操作,我发现测试resetButtons
组件中的方法非常困难。特别是考虑到它没有任何方法。
我尝试resetButtons
在组件外部定义方法,然后将其导出,以便开玩笑测试可以导入它。但是我一直无法测试该方法,因为它似乎希望它是一个间谍或模拟,而不是方法本身。( Matcher error: received value must be a mock or spy function
)
这是反应功能组件:
import React from 'react';
import PropTypes from 'prop-types';
import classes from './MainButton.module.scss';
const MainButton = (props) => {
const resetButtons = () => {
const elements = document.getElementsByClassName('mainButton');
for (let i = 0; i < elements.length; i += 1) {
elements[i].classList.remove('active');
}
};
const handleClick = (event) => {
if (!event.target.classList.contains('active')) {
resetButtons();
event.target.classList.add('active');
props.setVisualState(props.className.split('-')[0]);
}
};
return (
<button
onClick={handleClick}
type="button"
className={`${classes.mainButton} ${props.className}`}
>
{props.children}
</button>
);
};
MainButton.propTypes = {
children: PropTypes.node,
className: PropTypes.string,
setVisualState: PropTypes.func.isRequired,
};
MainButton.defaultProps = {
children: 'Button',
className: '',
};
export default MainButton;
这是测试
import React from 'react';
import { shallow } from 'enzyme';
import MainButton from './MainButton';
describe('MainButton', () => {
const domElement = { classList: { contains: jest.fn(), remove: jest.fn(), add: jest.fn() } };
const setVisualStateMock = jest.fn();
const mainButton = shallow(<MainButton setVisualState={setVisualStateMock} />);
it(' is rendered properly', () => {
expect(mainButton).toMatchSnapshot();
});
describe('when clicked', () => {
beforeEach(() => {
mainButton.find('button').simulate('click', { target: domElement });
});
it('it runs `classlist.add` to assign `active` class', () => {
expect(domElement.classList.add).toHaveBeenCalledWith('active');
});
it('it runs set visual state to update `Allergen` container `state`', () => {
expect(setVisualStateMock).toHaveBeenCalled();
});
});
});
目前覆盖率报告报告了 92% 的覆盖率,但分支为 50,导致问题的线路在第 9 行(elements[i].classList.remove('active');
line.
我知道在 90% 的情况下,我可能应该继续前进,但这是我想要弄清楚的事情。感觉像解决这个问题会让我得到更好的测试。
希望大家能帮忙!
解决方案
自己在 DOM 中摸索是一种反模式。这就是 React 的工作。与其一起操作 dom,不如使用target.classList.add
state 属性来保存您的输入当前处于活动状态的状态。然后,在渲染时你可以说className={isActiveInput ? "active": null}
.
因为状态不是特定于您的 MainButton 组件,所以您将提升状态。如果您在父级中的某处有状态,则不必通过类名粗略地搜索 DOM 元素并自己操作 dom。
简单地说,React 的规则是:你定义事物应该是什么样子,React 会注意你的定义在 dom 中成为现实。如果你自己操作 DOM - 你做错了。
当所有这些都完成后,你的测试就完全没有问题了,因为你所要做的就是提供适当的状态和道具,这很容易,并检查你的回调是否被触发 onClick。
编辑:高级版本将使用Context,但我会先使用状态提升。
推荐阅读
- c# - 如何将这些名称“插入”到我要更新的方法/变量中?
- c# - 无法加载文件或程序集..即使它在正确的位置并且构建良好
- spring - SpringBoot 创建框架启动库
- r - R - 帮助从 0 构建时间列?
- php - 对特定分类有问题循环
- mysql - 数据库架构:当记录具有不同数量的属性(列)
- python - beautifulsoup select 方法返回回溯
- android - 是什么导致 Google Play 开发者控制台中出现此警告:“我们检测到您的应用正在使用旧版本的 Google Play 开发者 API”
- php - 为什么添加标题后无法获取 POSTFIELDS?
- react-native - 如何在本机反应中跨组件全局调用一种方法