React中@azure/msal-react私有路由单元测试失败求助
解决@azure/msal-react私有路由单元测试失败问题
先确认私有路由的正确实现
先确保你的私有路由组件逻辑正确,这是测试的基础:
import { Navigate, Outlet } from 'react-router-dom'; import { useMsal } from '@azure/msal-react'; const PrivateRoute = () => { const { accounts } = useMsal(); const isAuthenticated = accounts.length > 0; return isAuthenticated ? <Outlet /> : <Navigate to="/login" replace />; }; export default PrivateRoute;
第一种测试:无法找到目标组件的解决
问题核心
测试时未正确挂载路由上下文与Msal认证上下文,导致私有路由无法正常渲染目标组件。
修复后的测试用例(React Testing Library)
import { render, screen } from '@testing-library/react'; import { MemoryRouter, Routes, Route } from 'react-router-dom'; import { MsalProvider } from '@azure/msal-react'; import { PublicClientApplication } from '@azure/msal-browser'; import PrivateRoute from './PrivateRoute'; import ProtectedComponent from './ProtectedComponent'; // 你的受保护业务组件 // 初始化测试用MSAL实例 const mockMsalInstance = new PublicClientApplication({ auth: { clientId: 'test-client-id', authority: 'https://login.microsoftonline.com/common' } }); test('已认证状态下渲染受保护组件', () => { // 模拟已登录账号 jest.spyOn(mockMsalInstance, 'getAllAccounts').mockReturnValue([{ homeAccountId: 'test-account' }]); render( <MsalProvider instance={mockMsalInstance}> <MemoryRouter initialEntries={['/protected']}> <Routes> <Route element={<PrivateRoute />}> <Route path="/protected" element={<ProtectedComponent />} /> </Route> <Route path="/login" element={<div>登录页</div>} /> </Routes> </MemoryRouter> </MsalProvider> ); // 验证目标组件渲染 expect(screen.getByText('受保护内容')).toBeInTheDocument(); }); test('未认证状态下跳转到登录页', () => { // 模拟无登录账号 jest.spyOn(mockMsalInstance, 'getAllAccounts').mockReturnValue([]); render( <MsalProvider instance={mockMsalInstance}> <MemoryRouter initialEntries={['/protected']}> <Routes> <Route element={<PrivateRoute />}> <Route path="/protected" element={<ProtectedComponent />} /> </Route> <Route path="/login" element={<div>登录页</div>} /> </Routes> </MemoryRouter> </MsalProvider> ); // 验证跳转后的登录页渲染 expect(screen.getByText('登录页')).toBeInTheDocument(); });
第二种测试:Mock Client传入MsalProvider报错的解决
常见报错原因及修复方案
Mock实例缺少必要方法
不要手动构造不完整的普通对象作为mock,直接基于PublicClientApplication实例mock关键方法,或全局mock整个模块:// 全局mock @azure/msal-browser jest.mock('@azure/msal-browser', () => ({ PublicClientApplication: jest.fn().mockImplementation(() => ({ getAllAccounts: jest.fn().mockReturnValue([]), acquireTokenSilent: jest.fn().mockResolvedValue({ accessToken: 'test-token' }), // 根据私有路由逻辑补充需要的方法 })) }));MsalProvider实例类型不匹配
必须传入PublicClientApplication的实例(真实或jest mock生成的实例),不能传入普通对象,否则会触发内部类型校验报错。异步认证状态未等待
如果私有路由包含异步认证逻辑,需使用waitFor等待状态更新:import { waitFor } from '@testing-library/react'; test('异步认证完成后渲染受保护组件', async () => { const mockMsalInstance = new PublicClientApplication({ auth: { clientId: 'test' } }); // 模拟异步获取账号的过程 jest.spyOn(mockMsalInstance, 'getAllAccounts').mockImplementation(() => { setTimeout(() => mockMsalInstance.getAllAccounts.mockReturnValue([{ homeAccountId: 'test' }]), 100); return []; }); render( <MsalProvider instance={mockMsalInstance}> <MemoryRouter initialEntries={['/protected']}> <Routes> <Route element={<PrivateRoute />}> <Route path="/protected" element={<ProtectedComponent />} /> </Route> </Routes> </MemoryRouter> </MsalProvider> ); await waitFor(() => { expect(screen.getByText('受保护内容')).toBeInTheDocument(); }); });
通用测试注意事项
- 所有路由组件必须包裹在
MemoryRouter中,避免依赖浏览器环境的window.location - 确保
MsalProvider的instance属性是有效的PublicClientApplication实例 - 使用
jest.spyOn精准mock单个方法,避免替换整个模块导致其他依赖逻辑失效 - 测试路由跳转时,通过
initialEntries设置初始路由,验证最终渲染的目标组件
内容的提问来源于stack exchange,提问作者SDK
相关产品推荐
相关产品推荐

