在反应测试库中没有从 .toHaveBeenCalledTimes() 获得预期结果

Gam*_*ix1 7 unit-testing reactjs jestjs react-testing-library

无论如何,尝试测试函数在触发后是否被调用。当我从该函数中得到 a 时,它fireEvent正在工作。console.log.toHaveBeenCalledTimes(1)返回 0。我错过了什么?

\n\n

如果我handleLoginSubmit在父级中有该函数并将其作为支柱传递给子级,并且在测试中一切都会通过。但如果它在同一个组件中,则会失败。使用打字稿(如果有任何意义的话)。

\n\n

这是经过测试的

\n\n
import React, { FC } from 'react';\n\ntype Event = React.FormEvent<HTMLFormElement>;\n\ninterface Login {\n  handleLoginSubmit?: (event: Event) => React.ReactNode;\n}\n\nconst Login: FC<Login> = () => {\n  const handleLoginSubmit = (_event: Event) => {\n    console.log('Firing' ); // This is logged\n  };\n\n  return (\n    <form data-testid='form' onSubmit={(event) => handleLoginSubmit(event)}>\n      <input data-testid='email'/>\n      <input data-testid='password'/>\n      <button data-testid='login-button'>login</button>\n    </form>\n  );\n};\n\nexport default Login;\n
Run Code Online (Sandbox Code Playgroud)\n\n

我的提交测试

\n\n
  it('should handle ClickEvents', () => {\n\n    const handleLoginSubmit = jest.fn();\n\n    const { getByTestId } = render(<Login/>);\n\n    expect(getByTestId('login-button')).toBeTruthy();\n\n    fireEvent.submit(getByTestId('form'));\n\n    expect(handleLoginSubmit).toHaveBeenCalledTimes(1);\n\n  });\n
Run Code Online (Sandbox Code Playgroud)\n\n

错误信息

\n\n
 \xe2\x97\x8f Login page \xe2\x80\xba should handle ClickEvents\n\n    expect(jest.fn()).toHaveBeenCalledTimes(expected)\n\n    Expected number of calls: 1\n    Received number of calls: 0\n\n      32 |     fireEvent.submit(getByTestId('form'));\n      33 | \n    > 34 |     expect(handleLoginSubmit).toHaveBeenCalledTimes(1);\n         |                               ^\n      35 | \n      36 |   });\n      37 | });\n\n      at Object.it (__tests__/components/Login.test.tsx:34:31)\n
Run Code Online (Sandbox Code Playgroud)\n

sli*_*wp2 8

您无法断言是否handleLoginSubmit直接调用该函数。因为它是在LoginSFC 的私有范围内定义的。您无法模拟或监视此函数,因为您无法访问它。因此,您需要间接测试它。console.log由于您在这个函数中使用,我们可以监视console.log. 如果它被调用,则意味着该handleLoginSubmit函数已被调用。

\n

例如

\n

index.tsx:

\n
import React, { FC } from "react";\n\ntype Event = React.FormEvent<HTMLFormElement>;\n\ninterface Login {\n  handleLoginSubmit?: (event: Event) => React.ReactNode;\n}\n\nconst Login: FC<Login> = () => {\n  const handleLoginSubmit = (_event: Event) => {\n    console.log("Firing");\n  };\n\n  return (\n    <form data-testid="form" onSubmit={event => handleLoginSubmit(event)}>\n      <input data-testid="email" />\n      <input data-testid="password" />\n      <button data-testid="login-button">login</button>\n    </form>\n  );\n};\n\nexport default Login;\n
Run Code Online (Sandbox Code Playgroud)\n

index.spec.tsx:

\n
import { render, fireEvent } from "@testing-library/react";\nimport Login from "./";\nimport React from "react";\n\nit("should handle ClickEvents", () => {\n  const logSpy = jest.spyOn(console, "log");\n  const { getByTestId } = render(<Login />);\n  expect(getByTestId("login-button")).toBeTruthy();\n  fireEvent.submit(getByTestId("form"));\n  expect(logSpy).toHaveBeenCalledTimes(1);\n});\n
Run Code Online (Sandbox Code Playgroud)\n

100%覆盖率的单元测试结果:

\n
 PASS  src/stackoverflow/59162138/index.spec.tsx\n  \xe2\x9c\x93 should handle ClickEvents (42ms)\n\n  console.log node_modules/jest-mock/build/index.js:860\n    Firing\n\n-----------|----------|----------|----------|----------|-------------------|\nFile       |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |\n-----------|----------|----------|----------|----------|-------------------|\nAll files  |      100 |      100 |      100 |      100 |                   |\n index.tsx |      100 |      100 |      100 |      100 |                   |\n-----------|----------|----------|----------|----------|-------------------|\nTest Suites: 1 passed, 1 total\nTests:       1 passed, 1 total\nSnapshots:   0 total\nTime:        3.987s, estimated 9s\n
Run Code Online (Sandbox Code Playgroud)\n

源代码:https ://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59162138

\n

  • 测试组件的状态,不测试SFC中定义的函数的实现细节。这意味着函数(钩子、事件处理程序等)将改变组件的状态。您应该断言组件的状态。 (2认同)
  • @GameAtrix 是的。此外,如果更改事件处理程序是在 SFC 内的私有范围内定义的,则您无法访问它。所以,你不能添加一个间谍并嘲笑它。您需要测试组件的更新状态/属性(实际输入字段中的输出) (2认同)