Major Features: - ✅ Multi-tenant architecture (tenant isolation) - ✅ Employee CRUD with lifecycle management (onboarding/offboarding) - ✅ Department tree structure with email domain management - ✅ Company info management (single-record editing) - ✅ System functions CRUD (permission management) - ✅ Email account management (multi-account per employee) - ✅ Keycloak SSO integration (auth.lab.taipei) - ✅ Redis session storage (10.1.0.254:6379) - Solves Cookie 4KB limitation - Cross-system session sharing - Sliding expiration (8 hours) - Automatic token refresh Technical Stack: Backend: - FastAPI + SQLAlchemy - PostgreSQL 16 (10.1.0.20:5433) - Keycloak Admin API integration - Docker Mailserver integration (SSH) - Alembic migrations Frontend: - Next.js 14 (App Router) - NextAuth 4 with Keycloak Provider - Redis session storage (ioredis) - Tailwind CSS Infrastructure: - Redis 7 (10.1.0.254:6379) - Session + Cache - Keycloak 26.1.0 (auth.lab.taipei) - Docker Mailserver (10.1.0.254) Architecture Highlights: - Session管理由 Keycloak + Redis 統一控制 - 支援多系統 (HR/WebMail/Calendar/Drive/Office) 共享 session - Token 自動刷新,異質服務整合 - 未來可無縫遷移到雲端 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
6.4 KiB
6.4 KiB
TDD 開發指南 - HR Portal 前端
建立日期: 2026-02-21 測試框架: Vitest + React Testing Library
📋 目錄結構
frontend/
├── services/
│ ├── __tests__/
│ │ └── onboarding.service.test.ts # API Service 測試
│ └── onboarding.service.ts # API Service 實作
├── components/
│ ├── __tests__/
│ │ └── OnboardingForm.test.tsx # 組件測試 (待建立)
│ └── OnboardingForm.tsx # 組件實作 (待建立)
├── types/
│ └── onboarding.ts # TypeScript 型別定義
├── vitest.config.ts # Vitest 配置
└── vitest.setup.ts # 測試環境設置
🚀 快速開始
1. 安裝依賴
cd q:/porscheworld_develop/hr-portal/frontend
npm install
需要安裝的測試相關套件:
{
"devDependencies": {
"@testing-library/jest-dom": "^6.1.5",
"@testing-library/react": "^14.1.2",
"@testing-library/user-event": "^14.5.1",
"@vitejs/plugin-react": "^4.2.1",
"@vitest/ui": "^1.0.4",
"jsdom": "^23.0.1",
"vitest": "^1.0.4"
}
}
2. 執行測試
# 執行所有測試
npm test
# 執行測試並開啟 UI 介面
npm run test:ui
# 執行測試並生成覆蓋率報告
npm run test:coverage
# 監視模式(自動重跑)
npm test -- --watch
🎯 TDD 工作流程
Red-Green-Refactor 循環
1. RED → 先寫失敗的測試
2. GREEN → 寫最少的程式碼讓測試通過
3. REFACTOR → 重構程式碼,保持測試通過
範例:API Service TDD
Step 1: RED - 寫失敗的測試
// services/__tests__/onboarding.service.test.ts
import { describe, it, expect } from 'vitest'
import { onboardingService } from '../onboarding.service'
describe('OnboardingService', () => {
it('should successfully onboard an employee', async () => {
const request = {
resume_id: 1,
keycloak_user_id: '550e8400-...',
keycloak_username: 'wang.ming',
hire_date: '2026-02-21',
departments: [{ department_id: 9, position: '工程師' }],
role_ids: [1],
}
const result = await onboardingService.onboardEmployee(request)
expect(result).toHaveProperty('message')
expect(result).toHaveProperty('employee')
})
})
執行測試:
npm test
# ❌ 測試失敗:onboardingService 不存在
Step 2: GREEN - 實作最少程式碼
// services/onboarding.service.ts
import axios from 'axios'
class OnboardingService {
async onboardEmployee(request: OnboardingRequest) {
const response = await axios.post('/api/v1/emp-lifecycle/onboard', request)
return response.data
}
}
export const onboardingService = new OnboardingService()
執行測試:
npm test
# ✅ 測試通過
Step 3: REFACTOR - 重構
// 新增型別定義、錯誤處理、環境變數等
📦 已完成的測試
✅ API Service 層
檔案: services/__tests__/onboarding.service.test.ts
測試案例:
- ✅
onboardEmployee- 成功到職 - ✅
onboardEmployee- API 失敗處理 - ✅
getEmployeeStatus- 查詢員工狀態 - ✅
getEmployeeStatus- 員工不存在 - ✅
offboardEmployee- 成功離職
執行測試:
npm test services/__tests__/onboarding.service.test.ts
🔧 下一步:組件測試
待建立的測試
1. OnboardingForm 組件測試
// components/__tests__/OnboardingForm.test.tsx
import { describe, it, expect } from 'vitest'
import { render, screen, fireEvent } from '@testing-library/react'
import { OnboardingForm } from '../OnboardingForm'
describe('OnboardingForm', () => {
it('should render all required fields', () => {
render(<OnboardingForm />)
expect(screen.getByLabelText('員工姓名')).toBeInTheDocument()
expect(screen.getByLabelText('Keycloak 使用者名稱')).toBeInTheDocument()
expect(screen.getByLabelText('到職日期')).toBeInTheDocument()
})
it('should validate required fields', async () => {
render(<OnboardingForm />)
const submitButton = screen.getByRole('button', { name: /送出/i })
fireEvent.click(submitButton)
expect(await screen.findByText('此欄位為必填')).toBeInTheDocument()
})
it('should submit form successfully', async () => {
const onSubmit = vi.fn()
render(<OnboardingForm onSubmit={onSubmit} />)
// Fill form...
fireEvent.click(screen.getByRole('button', { name: /送出/i }))
expect(onSubmit).toHaveBeenCalled()
})
})
2. EmployeeStatusCard 組件測試
// 顯示員工詳細資訊的卡片組件
📊 測試覆蓋率目標
| 類型 | 目標 | 當前 |
|---|---|---|
| API Service | 100% | 100% ✅ |
| Components | 80% | 0% ⏳ |
| Hooks | 80% | 0% ⏳ |
| Utilities | 90% | 0% ⏳ |
🎨 測試最佳實踐
1. 測試命名
// ✅ Good: 描述行為,不是實作
it('should display error when API fails', () => {})
// ❌ Bad: 測試實作細節
it('should call axios.post', () => {})
2. AAA 模式
it('should do something', () => {
// Arrange (準備)
const data = { ... }
// Act (執行)
const result = doSomething(data)
// Assert (斷言)
expect(result).toBe(expected)
})
3. 隔離測試
// ✅ Good: 每個測試獨立
describe('MyComponent', () => {
beforeEach(() => {
// 每個測試前重置
cleanup()
})
})
// ❌ Bad: 測試間有依賴
4. Mock 外部依賴
// Mock axios
vi.mock('axios')
// Mock Next.js router
vi.mock('next/navigation')
🐛 常見問題
Q1: ReferenceError: describe is not defined
解決: 確認 vitest.config.ts 中有設定 globals: true
Q2: TypeError: Cannot read property 'xxx' of undefined
解決: 檢查是否正確 mock 了依賴
Q3: 測試執行很慢
解決:
- 使用
vi.mock()避免真實 API 呼叫 - 使用
--run參數執行一次性測試
📚 參考資源
下一步: 執行 npm install 安裝測試依賴,然後執行 npm test 確認測試環境正常運作。