React组件添加AppNavbar后测试失败,Mock组件报错求解决
问题原因
- 空值访问报错:AppNavbar组件初始化时直接执行
AuthService.getCurrentlyAuthenticatedUser().userId,测试环境无登录用户,localStorage.getItem("authenticatedUser")返回null,JSON解析后仍是null,访问.userId触发Cannot read properties of null (reading 'userId')错误。 - Mock方法错误:
mockComponent(AppNavbar)并非Jest或React Testing Library的官方API,导致出现Cannot read properties of undefined (reading 'render')异常。
方案1:直接Mock AppNavbar组件
用Jest的jest.mock替换整个AppNavbar,让它在测试中渲染一个空组件,彻底跳过原组件的逻辑:
修改LoginForm.test.js,在文件顶部添加Mock:
import AppNavbar from './AppNavbar'; // 确保路径与项目一致 // 将AppNavbar Mock为一个空占位组件 jest.mock('./AppNavbar', () => () => <div data-testid="mock-navbar"></div>); // 后续测试代码保持不变 const server = setupServer( rest.post(config.URL + "/auth/login", (req, res, ctx) => { return res(ctx.json("Success.")) }), ) // ... 其余测试逻辑
这样LoginForm渲染时会使用Mock的空组件,不会触发原AppNavbar里的AuthService调用,测试即可正常执行。
方案2:Mock AuthService的返回值
如果需要保留AppNavbar的渲染逻辑,或者测试依赖Navbar存在,可以Mock AuthService的getCurrentlyAuthenticatedUser方法,返回模拟用户数据:
方式A:Mock AuthService模块
假设AuthService是独立模块,在LoginForm.test.js中添加:
import AuthService from './AuthService'; // 路径需匹配项目 // Mock指定方法,保留其他方法原有实现 jest.mock('./AuthService', () => ({ ...jest.requireActual('./AuthService'), getCurrentlyAuthenticatedUser: () => ({ userId: 'test-123' }) // 返回带userId的模拟对象 }));
方式B:直接Mock localStorage
不想修改AuthService的话,可在测试前给localStorage添加模拟用户数据:
beforeEach(() => { // 设置模拟认证用户数据 localStorage.setItem('authenticatedUser', JSON.stringify({ userId: 'test-123', accessToken: 'fake-token' })); }); afterEach(() => { // 测试后清空localStorage,避免影响其他用例 localStorage.clear(); });
额外优化:让AppNavbar更健壮(推荐)
为避免生产环境也出现类似错误(比如未登录用户访问页面),给AppNavbar添加空值判断:
修改AppNavbar.js:
export const AppNavbar = () => { // 先获取用户对象,用可选链安全访问userId const authenticatedUser = AuthService.getCurrentlyAuthenticatedUser(); const userId = authenticatedUser?.userId; const [isOpen, setIsOpen] = useState(false); const toggle = () => setIsOpen(!isOpen); return ( <> <Navbar className="NavBar" color="dark" dark expand="sm"> <NavbarBrand id="NavBarBrand" href="/">Po</NavbarBrand> <NavbarToggler onClick={toggle} /> <Collapse isOpen={isOpen} navbar> <Nav navbar className="ml-auto" id="Nav"> {/* 仅当userId存在时渲染Profile链接 */} {userId && ( <NavItem className="NavLink"> <NavLink href={`/profile/${userId}`} className="NavLink">Profile</NavLink> </NavItem> )} {/* 其余导航项保持不变 */} <UncontrolledDropdown nav inNavbar> <DropdownToggle nav caret> Play </DropdownToggle> <DropdownMenu end> <DropdownItem tag="a" href={'/players'}> Players </DropdownItem> <DropdownItem tag="a" href={'/top-players'}> Top Players </DropdownItem> </DropdownMenu> </UncontrolledDropdown> {/* ... 其他下拉菜单 */} </Nav> </Collapse> </Navbar> </> ) }
这样即使无登录用户,AppNavbar也不会报错,而是自动隐藏Profile链接,同时测试稳定性也会提升。
内容的提问来源于stack exchange,提问作者devo9191
相关产品推荐
相关产品推荐

