如何用React Testing Library测试表单提交时的临时过渡加载文本?
问题原因
- 你当前的
doformStuff是同步执行逻辑,React会对同一个同步任务栈内的多次状态更新做批量合并处理,所以你先后调用的_setTitle('Submitting...')和_setTitle('Submitted perfectly.')会被合并,最终只会渲染最终状态,中间的提交中状态根本不会渲染到DOM上。 - 即便没有批量更新机制,同步代码的执行速度远快于测试断言的触发时机,等你执行断言语句时,finally块内的状态更新已经执行完成,自然拿不到中间状态。
解决方案
方案1:改造业务代码为异步(推荐)
大部分真实场景的表单提交都是异步接口请求,你可以把提交逻辑改成异步,用await Promise.resolve()让出主线程,让React先把提交中的状态渲染完成,再执行后续的表单处理逻辑:
const handleSubmit = async (e, _setTitle) => { e.preventDefault() _setTitle('Submitting...') // 让出主线程,触发React渲染提交中状态 await Promise.resolve() try { doformStuff(emailRef.current.value) } catch (err) { throw new Error(err) } finally { _setTitle('Submitted perfectly.') } }
如果你的doformStuff本身就是异步接口请求,直接加await等待它执行完成即可,不需要额外加上面的await Promise.resolve()。
方案2:调整测试用例,卡住异步执行时机
测试时把doformStuffmock成一个暂时不resolve的Promise,让提交逻辑停在try块内,不会走到finally的状态更新,此时就能断言到中间的提交中状态:
it('shows "submitting" when submitting', async () => { // mock doformStuff,让其保持pending状态,不立即执行完成 jest.spyOn(yourModule, 'doformStuff').mockImplementation(() => new Promise(() => {})) // arrange render(<MobileEmailCapture/>) // act const emailInput = screen.getByPlaceholderText('yourem@il.com') // userEvent的方法都是异步的,需要加await等待输入完成 await userEvent.type(emailInput, fakeEmail) fireEvent.submit(screen.getByTestId('form')) // assert 此时提交逻辑还未执行完成,可捕获到提交中状态 expect(screen.getByTestId('title')).toHaveTextContent('Submitting...') })
注意把代码里的yourModule替换成你实际导入doformStuff的模块名即可。
内容的提问来源于stack exchange,提问作者Josh Pittman
相关产品推荐
相关产品推荐

