使用 Enzyme 测试无状态 React 组件中的函数
Posted
技术标签:
【中文标题】使用 Enzyme 测试无状态 React 组件中的函数【英文标题】:Testing functions inside stateless React component with Enzyme 【发布时间】:2019-08-06 22:30:03 【问题描述】:我有一个无状态组件:
export default function TripReportFooter(props)
const tripReport, user, toggleFavorite, navigation = props;
handleShare = async slug =>
try
const result = await Share.share(
message: `Check out this Trip Report:\n/p/$slug/`
);
if (result.action === Share.sharedAction)
if (result.activityType)
else
// shared
else if (result.action === Share.dismissedAction)
catch (error)
alert(error.message);
;
handleFavorite = async id =>
const token = await AsyncStorage.getItem("token");
toggleFavorite(id, token);
;
return (
... // handleFavorite and handleShare called with TouchableOpacities.
);
它内部有两个函数,handleShare
和 handleFavorite
。我想测试这些函数被调用了,还有handleFavorite
调用了prop函数toggle favorite。
我试过wrapper.instance().handleFavorite()
,但由于它是一个无状态组件,它返回null
。
接下来 Stack Overflow 上的某个人建议使用这样的间谍:
wrapper = shallow(<TripReportFooter ...props handleFavorite=press />);
wrapper
.find("TouchableOpacity")
.at(0)
.simulate("press");
expect(press.called).to.equal(true);
但这返回了
'TypeError: 无法读取属性'equal' of undefined'。
调用这些函数的正确方法是什么?
【问题讨论】:
不是答案,只是观察到您得到了TypeError: Cannot read property 'equal' of undefined
,因为.to.equal(true)
是Chai
语法。由于您使用的是Jest
,它应该是.toBe(true)
。
你能把你的测试用例贴在这里codesandbox.io/s/m50o1ok8o9并发回网址吗?
不需要先获取实例吗?包装器。实例()?不确定。这就是我在类组件上测试函数的方式,但是我在函数式组件中遇到了问题,因为它们显然没有暴露出来。
这两个函数是如何触发的?请编辑您的帖子,添加更多代码
【参考方案1】:
您首先需要考虑要测试什么。是实现细节还是与组件的交互?后者是一种更好的思维方式和立场,所以我要做的就是从交互的角度测试组件。
我愿意(对于handleShare
):
-
模拟在 share 函数内部调用的 Share 对象方法;
选择我想点击/触摸的按钮
点击/触摸按钮
断言方法已被调用。
现在是handleFavorite
:
-
模拟
AsyncStorage.getItem
;
创建一个假的 toggleFavorite
函数,我将其作为道具传递;
选择我想点击/触摸的按钮
点击/触摸按钮
断言我的toggleFavorite
函数已被调用
如果您想单独测试这些功能,您必须将它们提取到组件外部并单独测试它们。但我不建议这样做,因为它不干净且需要额外的工作。
希望对您有所帮助!
【讨论】:
【参考方案2】:功能组件中的功能未在原型或功能组件实例上定义,您不能直接监视它们
这里的解决方案是测试各个函数的内部实现
例如,对于handleFavourite
函数,您可以模拟 AsynStorage 并为 toggleFavourite 传递一个模拟函数,然后在 TouchableOpacity onPress 模拟上调用它
您可以在这篇文章中查看如何模拟 AsyncStore: How to test Async Storage with Jest?
const mocktToggleFavourite = jest.fn();
wrapper = shallow(<TripReportFooter ...props toggleFavourite=mocktToggleFavourite />);
wrapper
.find("TouchableOpacity")
.at(0)
.simulate("press");
expect(mockToggleFavourite).toHaveBeenCalled();
同样,您可以通过首先模拟 Share.share
然后检查每个条件来测试 handleShare
中的各个功能。例如,您可以在 window.alert 上添加一个间谍并查看它是否被调用
const windowSpy = jest.spyOn(window, 'alert');
wrapper = shallow(<TripReportFooter ...props toggleFavourite=mocktToggleFavourite />);
//Simulate event that calls handleShare
// Mock Share to give required result
expect(windowSpy).toBeCalledWith(expectedValue);
【讨论】:
如果我们要测试函数的返回值怎么办?例如toCamelcase()
@johannchopin 在这种情况下,您会看到返回值正在被另一个函数使用,并将用于更新状态或调用父函数。然后的想法是不测试返回值的函数,而是测试接受该函数返回值的函数。编写这样一个测试用例将确保您的两个功能都被覆盖
是的,这适用于糟糕的逻辑,但如果我有多个链接函数,如 toMyStringFormat()
,调用 toMyCamelCasel()
,调用 toMyLowercase()
,仅测试 toMyStringFormat()
的结果并不准确。如果测试失败,我们不知道错误的确切来源
如果您提供此问题的答案,您将获得bounty
:)
@johannchopin 我希望有一个解决方案。将很快研究它。如果我找到任何东西会通知你以上是关于使用 Enzyme 测试无状态 React 组件中的函数的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Jest 和 Enzyme 中执行无状态组件内的函数以进行测试
当组件是无状态功能时,通过显示名称查找组件,使用 Enzyme
使用 Enzyme 进行 Storybook JS 集成测试 - 状态更改被覆盖