【问题标题】:Test that React prop method has been called with Jest测试是否已使用 Jest 调用了 React prop 方法
【发布时间】:2019-07-24 10:30:33
【问题描述】:

我有一个 Input 组件,它接受一个 prop 方法并在用户输入内容时调用它。代码本身按预期工作,但由于某些原因,测试失败。它认为没有调用 prop 方法。为什么会这样?出于测试目的,我使用 Jest 和 react-testing-library。

第二个问题。在实际应用中,我的想法是测试传递给该 prop 方法的参数。是否认为是实现测试(我知道我应该测试它)?

Input.js

export default function Input({ onChange }) {
  return <input onChange={onChange} />;
}

测试

import React from "react";
import { render, act, cleanup, fireEvent } from "react-testing-library";
import Input from "./input";

describe("Input tests", () => {
  afterEach(cleanup);

  it("Should call prop function", () => {
    const onChange = jest.fn();
    const { getByTestId } = render(<Input onChange={onChange} />);
    const input = getByTestId("input");

    act(() => {
      fireEvent.change(input, { target: { value: "Q" } });
    });

    expect(onChange).toHaveBeenCalled();
  });
});

https://codesandbox.io/s/y229669nvx

【问题讨论】:

  • 因为change意味着它的状态发生了变化,如果要触发change,直接赋值比如input.value="xxx"怎么样?

标签: reactjs unit-testing jestjs react-testing-library


【解决方案1】:

读完之后,好像是by design to not assert against events handlers。虽然看起来 to work in React 16.5,但是,使用 16.8.x 失败。如果您想测试这些功能,我建议您转向酶。

使用react-testing-library 测试失败(但是,您会注意到,在运行测试时,输入的值实际上会改变):https://codesandbox.io/s/n3rvy891n4

使用enzyme 测试成功:https://codesandbox.io/s/lx34ny41nl

【讨论】:

  • 非常感谢您的回答!我也开始考虑改用酶,现在我想我会这样做:)
  • 说实话,这可能是一个更好的举措。 react-testing-library 似乎通过强制您在 element 中查找 data attributestext 来使测试复杂化。而enzyme 提供通过idclassNametextpropelementcomponent ...等查找。
  • 澄清一下,data-testid 被认为是 RTL 的最后手段。查找元素的首选方法是查找用户将看到的文本、标签或其他内容
【解决方案2】:

您的测试不起作用的原因是您使用getByTestId 来查找您的元素。 getByTestId 查找具有 data-testid 属性的 DOM 节点。

为了使您的测试通过,您有多种选择。

您可以将data-testid 添加到您的input&lt;input data-testid="input" onChange={onChange} /&gt;。这会起作用,但是,最好尽可能避免使用测试 ID。

在实际应用中,您的输入会以label 呈现,我们可以利用这一点:

const { getByLabelText } = render(
  <label>
    My input
    <Input onChange={onChange} />
  </label>
)
const input = getByLabelText('My input')

另一种解决方案是使用container,它是render 返回的值之一。它是一个 DOM 节点——就像 RTL 中的所有其他东西一样——所以你可以使用常用的 DOM API:

const { container } = render(<Input onChange={onChange} />)
// Any of these would work
const input = container.firstChild
const input = container.querySelector('input')

附带说明,我同意 RTL 测试与 Enzyme 相比似乎更复杂。这是有充分理由的。 RTL 推动您测试您的应用程序,就好像它是一个黑盒一样。一开始这有点难,但最终会带来更好的测试。

另一方面,Enzyme 在默认情况下会模拟大多数事物,并允许您与组件实现进行交互。根据我的经验,这在开始时看起来更容易,但会产生脆弱的测试。

如果您需要入门帮助,我鼓励您加入 spectrum channel

【讨论】:

  • 感谢您的回答!正如我在问题中所写,expect(onChange).toHaveBeenCalled() 断言存在问题。我可以很容易地找到一个输入元素,我可以编辑它的值并查看 DOM 中的变化。但是根据 RTL,永远不会调用 prop 方法。
  • 如果我运行你的代码和框,我看到的错误是Unable to find an element by: [data-testid="input"]。如果我应用我建议的其中一项更改,它就可以正常工作
  • 只是为了确定,我们是否在谈论这个沙盒:codesandbox.io/s/y229669nvx?我刚刚再次打开它,我看到了这个错误:expect(jest.fn()).toHaveBeenCalled() Expected mock function to have been called.
  • 是的,这就是沙盒。您可能会看到一些尚未推送的本地更改。我看到的错误是Unable to find an element by: [data-testid="input"],如果您查看您发布的代码,这是有道理的
  • @Gpx - 我已经将它包含在我的测试中(在第一个沙箱中)。它仍然失败。如前所述,创建者声明不要针对事件处理程序进行断言。
猜你喜欢
  • 2021-11-27
  • 2019-12-12
  • 2021-09-06
  • 1970-01-01
  • 2021-01-07
  • 1970-01-01
  • 2020-05-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多