开玩笑无法读取 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(() => Promise)
(返回一些承诺)和await waitFor(()=> ...)
时遇到了一些问题。
如果您使用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.assertions
和return
!
参考: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”的主要内容,如果未能解决你的问题,请参考以下文章
next-i18next 单元测试错误“无法读取未定义的属性语言”,带有链接和玩笑