使用React Testing Library测试时无法识别window.location变更问题
问题根因
- 你测试中使用的
createMemoryHistory是React Router提供的内存路由实现,所有路由状态都存储在内存中,不会和浏览器的window.location做同步,所以哪怕你用Router包裹了组件,memory history的状态变化也不会反应到window.location上,反过来你直接修改window.location也不会同步给memory history。 - 常用的前端测试环境(如Jsdom)对window.location的修改有严格限制,直接调用
window.location.replace不会真的修改当前页面路径,甚至可能被跨域规则静默拦截,所以直接拿window.location.pathname做断言肯定拿不到预期值。
解决方案1:改用React Router内置路由跳转(推荐)
不要直接操作原生window.location,改用React Router提供的useNavigate钩子做跳转,所有路由操作都走统一的history实例,测试时可以直接通过memory history捕捉路径变化。
修改后的组件代码
import { useLocation, useNavigate } from 'react-router-dom'; const My404 = () => { const location = useLocation(); const navigate = useNavigate(); useEffect(() => { // 加replace:true对应原replace的无历史记录跳转行为 navigate(`/404${location.pathname}`, { replace: true }); }, [navigate, location.pathname]); // ...其余逻辑 }
修改后的测试代码
it('Should add /404 to start of pathname', async () => { const history = createMemoryHistory() history.push('/some-route') render( <Router history={history}> <My404 /> </Router> ); // 直接断言history的当前路径即可 await waitFor(() => expect(history.location.pathname).toBe('/404/some-route')); });
解决方案2:必须保留原生window.location操作时,直接mock跳转方法
如果你一定要保留window.location.replace的写法,不需要真的验证浏览器跳转行为,只需要验证组件调用该方法时传入的参数正确即可:
修改后的测试代码
it('Should add /404 to start of pathname', async () => { // mock window.location.replace方法 const mockReplace = jest.spyOn(window.location, 'replace').mockImplementation(() => {}); const history = createMemoryHistory() history.push('/some-route') render( <Router history={history}> <My404 /> </Router> ); // 断言replace方法被调用且参数符合预期 await waitFor(() => expect(mockReplace).toHaveBeenCalledWith('/404/some-route')); // 测试结束后恢复原方法,避免影响其他用例 mockReplace.mockRestore(); });
内容的提问来源于stack exchange,提问作者physicsboy
相关产品推荐
相关产品推荐

