开玩笑无法读取 null 的属性“createEvent”

Posted

技术标签:

【中文标题】开玩笑无法读取 null 的属性“createEvent”【英文标题】:Jest Cannot read property 'createEvent' of null 【发布时间】: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 函数而引发了错误。如果我只用一个动作离开我的测试,它不会崩溃(问题是我需要使用两次动作)。有可能吗? 【参考方案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 测试返回一个承诺,因此需要 await。 https://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。

【讨论】:

以上是关于开玩笑无法读取 null 的属性“createEvent”的主要内容,如果未能解决你的问题,请参考以下文章

TypeError:无法使用玩笑读取未定义的属性“原型”

next-i18next 单元测试错误“无法读取未定义的属性语言”,带有链接和玩笑

“无法读取未定义的属性”错误消息是不是已更改?

无法读取 null 的属性(读取“类型”)

TypeError:无法读取 null 的属性(读取“发射”)

无法读取 null 的属性“图像”