使用 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.
  );

它内部有两个函数,handleSharehandleFavorite。我想测试这些函数被调用了,还有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 集成测试 - 状态更改被覆盖

使用 Jest/Enzyme 在 React 功能组件中测试封闭组件

如何使用 Enzyme 测试 React 组件属性的样式

使用 React Suspense 和 React.lazy 子组件进行 Jest/Enzyme 类组件测试