【问题标题】:Jest Cannot read property 'createEvent' of null开玩笑无法读取 null 的属性“createEvent”
【发布时间】:2020-06-15 16:28:00
【问题描述】:

我试图模拟被拒绝的值并得到这个错误。奇怪的是,这种构造在“成功”addUser.mockImplementation(value => jest.fn().mockResolvedValue(value)) 的情况下有效,但是当我尝试对拒绝执行相同的技巧时,它不起作用并显示“无法读取 null 的属性‘createEvent’”

这是我的测试用例

it('receives invalid value and throws an error', async () => {
  addUser.mockImplementation(() =>
    jest.fn().mockRejectedValue(new Error('Sample error'))
  )

  const enqueueSnackbar = jest.fn()
  useSnackbar.mockReturnValue({ enqueueSnackbar })

  const { emailInput, form, submitButton } = setup()

  await act(async () => {
    fillIn(emailInput, 'sample@mail.com')
  })

  expect(emailInput.value).toBe('sample@mail.com')
  expect(submitButton).toHaveProperty('disabled', false)

  await act(async () => {
    fireEvent.submit(form)
  })

  expect(enqueueSnackbar).toHaveBeenCalledTimes(1)
  expect(enqueueSnackbar).toHaveBeenCalledWith(`Sample error`, {
    variant: 'error'
})})

有谁知道如何让它工作?

【问题讨论】:

  • 真正的addUser函数长什么样子?看起来你可能想要adduser.mockImplementation(() => Promise.reject(new Error('Sample error'))),这意味着“当调用添加用户时,返回一个带有示例错误的被拒绝承诺”,而当前的测试代码意味着“当调用添加用户时,返回一个函数,当它被调用时,返回一个带有示例错误的被拒绝承诺”。我猜你想要第一个,但如果没有看到 addUser 函数就很难知道。
  • addUser.mockRejectedValue(new Error('Sample error'))addUser.mockImplementation(() => Promise.reject(new Error('Sample error'))) 相同
  • 我遇到了同样的问题,我认为由于重复了 act 函数而引发了错误。如果我只用一个动作离开我的测试,它不会崩溃(问题是我需要使用两次动作)。有可能吗?

标签: jestjs react-testing-library


【解决方案1】:

这似乎是当有人用 Google 搜索“无法读取 null 的属性 'createEvent'”时发现的第一个问题,所以把这个答案留给那些读者:

对我来说,这个错误是在测试过程中出现的。

在执行一系列测试时,某些测试或其他测试曾经因此错误而失败,没有任何迹象表明哪里出了问题。但结果证明不是测试而是组件本身:

这是一个未模拟的 API 调用。

在钩子中进行了 API 调用,并且该钩子在测试失败的组件中使用。显然,Jest 在完成测试后清理了所有内容,当调用返回时,它什么也没找到,所以出错了。

模拟钩子解决了这个问题。

如果有人遇到此类错误,请确保模拟您拥有的任何异步逻辑,尤其是当它返回时与 DOM 交互时。

【讨论】:

  • 添加到这个。我进行了一次没有发生异步提取的测试,但仍然出现此错误。我的问题是我刚刚在 5 秒的超时间隔内进行了测试。因此,在我单击一个按钮和事件处理周期完成之间,我收到了这个错误,令人惊讶的是,在同一个地方始终如一。然后测试在超时时失败,拆除了 DOM,但随后在我的测试中给出了这个错误而不是超时错误。
  • 就我而言,我只是忘记添加一些“等待”
  • @mrvisser,要是我几天前注意到你的评论就好了!这是我们面临的问题。有很多异步,但我们确信我们已经消除了任何泄漏,而且它只发生在我们的 CI 套件中,这些套件远没有我的开发机器强大。我在本地减少了 CPU,并且能够重现该错误。增加了开玩笑的超时时间,它在 CI 中通过了。优化测试会更好,但我们正在测试一个多步骤的入职流程,该流程本质上很长。如果我可以投票两次,我会的!
【解决方案2】:

类似于@alexandre_anicio 所说的。使用 findAllByText 查询时出现此错误。 expect(screen.findAllByText('word'))...

当我切换到getAllByText 时,错误消失了,测试通过了。 expect(screen.getAllByText('word'))...

如果我使用expect(await screen.findAllByText('word'))...,我注意到测试也通过了。

深入挖掘,这是因为 findBy 测试返回一个承诺,因此需要 awaithttps://testing-library.com/docs/guide-disappearance/#1-using-findby-queries

如果库抛出一个更好的错误会很好。

【讨论】:

  • 我通常使用 getBy 但这次我让我的 IDE 自动完成到:findBy 并且疯狂地试图在这个项目上设置测试? 我不敢相信搜索时缺少结果这个明显的在线错误。
  • 通过我的研究我发现:eslint-plugin-testing-library,它至少可以通过你的 linter 警告你这个问题。确保遵循其设置。
【解决方案3】:

这似乎对我有用,但我无法解释。尝试删除您的 act() 包装,并在调用 fireEvent 函数后立即使用 await。

  fireEvent.submit(form);
  await wait();

【讨论】:

  • 如果有人能解释这一点将非常高兴。此外,在 react-native-testing-library 文档中,他们说 wait 已被弃用,取而代之的是 waitFor。我只是要等待有人解释为什么?。看看我在那里做了什么
【解决方案4】:

当我遇到同样的错误消息时,我发现我在更新期望以包含await 后忘记将我的测试函数声明为async

【讨论】:

    【解决方案5】:

    waitFor 已经在后台使用了act,因此无需在那里使用act 块。

    我认识到您提到的错误,但我复制它的方式是使用 waitFor 而不使用 await,如下所示:

    it('works', async() => {
      render(<SomeComponent />);
      // (some setup here)
    
      waitFor(() => { // notice that we are not awaiting this promise
        expect(someChange).toBeInTheDocument();
      });
    });
    

    你可以试试

    it('receives invalid value and throws an error', async () => {
      addUser.mockImplementation(() =>
        jest.fn().mockRejectedValue(new Error('Sample error'))
      )
    
      const enqueueSnackbar = jest.fn()
      useSnackbar.mockReturnValue({ enqueueSnackbar })
    
      const { emailInput, form, submitButton } = setup()
    
      fillIn(emailInput, 'sample@mail.com') // This is using some fireEvent under the hood right?
    
      await waitFor(() => {
        expect(emailInput.value).toBe('sample@mail.com')
        expect(submitButton).toHaveProperty('disabled', false)
      });
      fireEvent.submit(form)
    
      await waitFor(() => {
        expect(enqueueSnackbar).toHaveBeenCalledTimes(1)
        expect(enqueueSnackbar).toHaveBeenCalledWith(`Sample error`, {
          variant: 'error'
        })
      });
    })
    

    【讨论】:

    • 我遇到了同样的问题,这对我有用,但后来我意识到这是误报。尝试将期望更改为应该失败的事情。
    • 这工作完美不知道 waitFor 使用幕后行为 :)
    【解决方案6】:

    我在同时使用mockImplementation(() =&gt; Promise)(返回一些承诺)和await waitFor(()=&gt; ...) 时遇到了一些问题。

    如果您使用react-testing-library,则可以使用findBy 查询解决此问题,这是getBy 查询和waitFor 的组合。唯一的缺点是您必须找到可以告诉您已调用模拟函数的可视化内容(文本、数据测试 ID、标签等...)。在您的代码上,您可以尝试这样的操作:

    it('receives invalid value and throws an error', async () => {
      addUser.mockImplementation(() =>
        jest.fn().mockRejectedValue(new Error('Sample error'))
      )
    
    await screen.findByText('Sample Error message reflected in your component')
    
    ... rest of your tests ...
    

    })

    【讨论】:

      【解决方案7】:

      如果错误是因为错误使用了 jest 的 findBy* 而不是 async/await 你也可以返回 promise:

      it('test', () => {
          expect.assertions(1);
          return screen
            .findByTestId('iiError')
            .then(elem =>
              expect(elem).toHaveTextContent(
                "This is error message"
              )
            );
      });
      

      不要忘记expect.assertionsreturn

      参考:https://jestjs.io/docs/tutorial-async

      【讨论】:

        【解决方案8】:
        1. await waitFor(() => expect(history.location.pathname).toBe('/auth'))
        2. await waitFor(() => expect(history.location.pathname)).toBe('/auth')
        

        这是关于别的东西,但同样的错误。花了2个小时,所以你不必:) 第二个括号放错地方是罪魁祸首

        【讨论】:

          【解决方案9】:

          我遇到了同样的问题,在我的情况下,罪魁祸首是渲染的 React 组件被 .unmount() 卸载。一个正在运行的 API 调用触发了一个回调,React 尝试更新已经卸载的 DOM。

          【讨论】:

            猜你喜欢
            • 2018-05-02
            • 2020-06-26
            • 2023-03-03
            • 2022-10-21
            • 2022-01-18
            • 2020-11-19
            • 2018-06-17
            • 2018-09-05
            • 1970-01-01
            相关资源
            最近更新 更多