diff --git a/src/routes/ProtectedRoute.test.jsx b/src/routes/ProtectedRoute.test.jsx
new file mode 100644
index 0000000..f422fd3
--- /dev/null
+++ b/src/routes/ProtectedRoute.test.jsx
@@ -0,0 +1,48 @@
+import { describe, it, expect, vi, beforeEach } from 'vitest';
+import { render, screen } from '@testing-library/react';
+import { MemoryRouter, Routes, Route } from 'react-router-dom';
+import ProtectedRoute from './ProtectedRoute';
+
+// 模拟 Navigate 组件
+vi.mock('react-router-dom', async () => {
+  const actual = await vi.importActual('react-router-dom');
+  return {
+    ...actual,
+    Navigate: () => <div data-testid="navigate-mock">重定向到登录页</div>,
+  };
+});
+
+describe('ProtectedRoute 组件', () => {
+  beforeEach(() => {
+    window.localStorage.clear();
+  });
+
+  it('未登录时应该重定向到登录页', () => {
+    render(
+      <MemoryRouter>
+        <ProtectedRoute>
+          <div>受保护的内容</div>
+        </ProtectedRoute>
+      </MemoryRouter>
+    );
+    
+    // 应该看到重定向组件
+    expect(screen.getByTestId('navigate-mock')).toBeInTheDocument();
+  });
+
+  it('已登录时应该显示子组件', () => {
+    // 模拟已登录状态
+    window.localStorage.setItem('token', 'fake-token');
+    
+    render(
+      <MemoryRouter>
+        <ProtectedRoute>
+          <div>受保护的内容</div>
+        </ProtectedRoute>
+      </MemoryRouter>
+    );
+    
+    // 应该看到受保护的内容
+    expect(screen.getByText('受保护的内容')).toBeInTheDocument();
+  });
+});
\ No newline at end of file
