如何在私有范围内测试功能?

Posted

技术标签:

【中文标题】如何在私有范围内测试功能?【英文标题】:How to test function in private scope? 【发布时间】:2020-12-04 17:00:01 【问题描述】:

当我尝试直接测试它时,我无法断言函数selectXHandler。因为它是在私有范围内定义的。我无法模拟或监视此功能,因为您无法访问它

export const CheckboxWrapper = (
  x,
  y,
  z,
  a = [],
: CheckboxWrapperProps) => 
  const State = useContext(Context);
  const [state, dispatch] = State;
  const  Items  = state;

  const selectXHandler = () => 
    const payload = 
      x,
      a,
    ;
    dispatch( type: action, payload );
  ;

  if (y) 
    return (
      <div className="mt5">
        <Checkbox
          checked=selectedItems[x] !== undefined
          label=z
          onChange=selectXHandler
        />
      </div>
    );
  
  return null;
;
    const selectXHandler = jest.fn();
    await fireEvent.click(checkbox);
    expect(checkbox).toBeChecked();

    expect(selectXHandler).toHaveBeenCalledTimes(1);

我收到以下错误:

    expect(jest.fn()).toHaveBeenCalledTimes(expected)

    Expected number of calls: 1
    Received number of calls: 0

【问题讨论】:

您应该测试面向公众的行为,而不是内部实现细节。例如。测试你的 UI 元素如何变化,而不是调用什么内部函数 我正在研究集成测试,检查功能是否被触发是其中的一部分。 @bsapaka 您可以改为模拟 redux 并检查它的调度是否已被调用以及使用什么参数。它也是你的组件“公共接口”,不像里面的一些私人功能。假设,您重命名了您的内部函数 - 应用程序可能因此而损坏吗?不。另一方面,如果您的组件开始调度操作“A”而不是“B”,它可能会破坏您的应用程序。所以用测试覆盖它是有意义的。 @contextq 无论是哪种测试,它都应该始终测试公共合约,而不是内部实现。您应该能够以满足正确行为的方式更改任何内部结构,并且测试应该仍然通过。 @skyboyer 我没有使用 redux,而是使用 usereducer 和 context 来创建类似的东西。你能用伪代码提出建议吗? 【参考方案1】:

正如上面在 cmets 中所讨论的,测试一些内部结构是一个糟糕的举动。想象一下这是可能的,我们重命名了该内部函数或将其内联或拆分为两个。我们测试它的测试会失败吗?确实。我们的应用程序会被破坏吗?当然不是。

我们应该绑定到公共接口。对于组件公共接口是:

    imports(如输入值) props(也是输入值) 渲染结果(输出值) 如果使用了上下文(也让我们将其视为输入值)

所以我看到了下一个方法:

    对于一些输入道具 我们模拟点击复选框 并验证上下文中的dispatch 是否已使用某些所需参数调用。

我不太了解 RTL,所以我的示例将是关于酶的。但我确信将其转换为适当的 RTL 选择器查询会很容易。

import Context from '../where/its/placed/someContext.js'; 


it('dispatch is called with A on checkbox is clicked', () => 
  const dispatch = jest.fn();
  const state = ; // or some initial state your test requires

  const ourComponent = mount(<Content.Provider value= dispatch, state >
    <CheckboxWrapper ...somePropsYouNeed /></Content.Provider>);
  ourComponent.find(label: 'some-label').simulate('change');

  expect(dispatch).toHaveBeenCalledWith( type: 'someType', payload: 'somePayload' );
  expect(dispatch).toHaveBeenCalledTimes(1);
);

【讨论】:

测试一些内部结构很糟糕 - 不是,这取决于具体情况。这对功能组件不利,因为它们不是为此而设计的。我经常看到“测试行为而不是执行”被视为宗教教条,这是 IMO 的一个非常糟糕的举动。 我们测试它的测试会失败吗?确实。我们的应用程序会被破坏吗?当然不是 - 为什么这是一件坏事?比应用程序损坏时不会失败的测试好很多倍。如果你在修复不应该失败的测试上浪费了几个小时,那就太糟糕了。如果断言内部结构可以节省您数小时的调试时间,那很好。 YMMV @EstusFlask 工程总是关于妥协,同意你的看法。但是,如果初学者开发人员从一开始就将自己绑定到测试内部,他们最终会得到两个结果:不可靠的测试由于脆弱性而浪费时间进行维护。还有一点“为什么这是一件坏事?” - 因为(当然,取决于情况)它使重构几乎不可能。很少有人喜欢痛苦,大多数人只是跳过重构,如果必须更新 10 多个测试以满足提升状态或移动方法到共享助手重构。 @EstusFlask 实际上我上面的示例也是一个折衷方案(我意识到这一点)并且部分依赖于内部结构(Context 如何工作的内部结构,针对dispatch 的论点的断言)。完全独立于内部的将是相当集成的,当我们在 Checkbox 上触发事件并验证 另一个 组件是否相应更改时,将呈现 &lt;Provider&gt;&lt;CheckboxWrapper /&gt;&lt;AnotherComponentThatConsumesContext /&gt;&lt;/Provider&gt;

以上是关于如何在私有范围内测试功能?的主要内容,如果未能解决你的问题,请参考以下文章

如何在模块化的angularjs范围内测试指令?

如何更新范围内的测试循环更改范围

为什么?在单元测试覆盖范围内显示的类即使未在测试目标中添加

如何获得“选择案例以测试范围内的每个单元格?

为自定义私有类实现 Equatable - Swift

如何检查整数是不是在范围内?