269 lines
6.4 KiB
Markdown
269 lines
6.4 KiB
Markdown
# 測試架構說明
|
|
|
|
## 概述
|
|
本測試架構採用三層測試策略:單元測試、整合測試、端到端測試。支援 xUnit 測試框架,並整合 Moq 進行 Mock 測試。
|
|
|
|
## 測試目錄結構
|
|
|
|
```
|
|
Tests/
|
|
├── README.md # 本文檔 - 測試架構說明
|
|
├── Unit/ # 單元測試
|
|
│ ├── Services/ # 服務層單元測試
|
|
│ ├── Controllers/ # 控制器單元測試
|
|
│ └── Repositories/ # Repository 單元測試
|
|
├── Integration/ # 整合測試
|
|
└── E2E/ # 端到端測試
|
|
```
|
|
|
|
## 測試框架與工具
|
|
|
|
### 核心測試框架
|
|
- **xUnit**: 主要測試框架
|
|
- **Moq**: Mock 物件框架
|
|
- **FluentAssertions**: 流暢斷言庫
|
|
- **Microsoft.AspNetCore.Mvc.Testing**: ASP.NET Core 測試支援
|
|
|
|
### 測試資料庫
|
|
- **SQLite In-Memory**: 用於快速單元測試
|
|
- **TestContainers**: 用於整合測試的容器化資料庫
|
|
|
|
## 單元測試規範
|
|
|
|
### 命名規範
|
|
```
|
|
{TestedMethod}_{Scenario}_{ExpectedResult}
|
|
|
|
例如:
|
|
- GetUserAsync_WithValidId_ReturnsUser()
|
|
- AnalyzeSentenceAsync_WithEmptyText_ThrowsArgumentException()
|
|
```
|
|
|
|
### 測試結構 (Arrange-Act-Assert)
|
|
```csharp
|
|
[Fact]
|
|
public async Task GetUserAsync_WithValidId_ReturnsUser()
|
|
{
|
|
// Arrange
|
|
var userId = 1;
|
|
var expectedUser = new User { Id = userId, Name = "Test User" };
|
|
var mockRepository = new Mock<IUserRepository>();
|
|
mockRepository.Setup(r => r.GetByIdAsync(userId))
|
|
.ReturnsAsync(expectedUser);
|
|
|
|
var service = new UserService(mockRepository.Object);
|
|
|
|
// Act
|
|
var result = await service.GetUserAsync(userId);
|
|
|
|
// Assert
|
|
result.Should().NotBeNull();
|
|
result.Id.Should().Be(userId);
|
|
result.Name.Should().Be("Test User");
|
|
}
|
|
```
|
|
|
|
## 服務層測試指南
|
|
|
|
### 測試重點服務
|
|
1. **GeminiService** - AI 服務核心功能
|
|
2. **AuthService** - 認證服務
|
|
3. **AnalysisService** - 分析服務
|
|
4. **RefactoredHybridCacheService** - 快取服務
|
|
|
|
### Mock 策略
|
|
- **外部 API 呼叫**: 使用 Mock HttpClient
|
|
- **資料庫操作**: Mock Repository 介面
|
|
- **檔案操作**: Mock 檔案系統相關服務
|
|
|
|
## 整合測試策略
|
|
|
|
### WebApplicationFactory
|
|
使用 ASP.NET Core 的 `WebApplicationFactory` 進行整合測試:
|
|
|
|
```csharp
|
|
public class IntegrationTestBase : IClassFixture<WebApplicationFactory<Program>>
|
|
{
|
|
protected readonly WebApplicationFactory<Program> Factory;
|
|
protected readonly HttpClient Client;
|
|
|
|
public IntegrationTestBase(WebApplicationFactory<Program> factory)
|
|
{
|
|
Factory = factory.WithWebHostBuilder(builder =>
|
|
{
|
|
builder.UseEnvironment("Testing");
|
|
builder.ConfigureServices(services =>
|
|
{
|
|
// 替換為測試資料庫
|
|
services.RemoveAll<DbContextOptions<DramaLingDbContext>>();
|
|
services.AddDbContext<DramaLingDbContext>(options =>
|
|
options.UseInMemoryDatabase("TestDb"));
|
|
});
|
|
});
|
|
|
|
Client = Factory.CreateClient();
|
|
}
|
|
}
|
|
```
|
|
|
|
### 測試資料管理
|
|
```csharp
|
|
public class TestDataSeeder
|
|
{
|
|
public static async Task SeedAsync(DramaLingDbContext context)
|
|
{
|
|
// 清理現有資料
|
|
context.Users.RemoveRange(context.Users);
|
|
|
|
// 新增測試資料
|
|
context.Users.Add(new User { Id = 1, Name = "Test User" });
|
|
|
|
await context.SaveChangesAsync();
|
|
}
|
|
}
|
|
```
|
|
|
|
## 測試執行命令
|
|
|
|
### 執行所有測試
|
|
```bash
|
|
dotnet test
|
|
```
|
|
|
|
### 執行特定類別的測試
|
|
```bash
|
|
dotnet test --filter "ClassName=GeminiServiceTests"
|
|
```
|
|
|
|
### 執行特定類型的測試
|
|
```bash
|
|
# 只執行單元測試
|
|
dotnet test --filter "Category=Unit"
|
|
|
|
# 只執行整合測試
|
|
dotnet test --filter "Category=Integration"
|
|
```
|
|
|
|
### 產生測試覆蓋率報告
|
|
```bash
|
|
dotnet test --collect:"XPlat Code Coverage"
|
|
```
|
|
|
|
## 測試資料工廠模式
|
|
|
|
### 實體建立工廠
|
|
```csharp
|
|
public static class TestDataFactory
|
|
{
|
|
public static User CreateUser(int id = 1, string name = "Test User")
|
|
{
|
|
return new User
|
|
{
|
|
Id = id,
|
|
Name = name,
|
|
Email = $"test{id}@example.com",
|
|
CreatedAt = DateTime.UtcNow
|
|
};
|
|
}
|
|
|
|
public static Flashcard CreateFlashcard(int id = 1, int userId = 1)
|
|
{
|
|
return new Flashcard
|
|
{
|
|
Id = id,
|
|
UserId = userId,
|
|
Front = "Test Front",
|
|
Back = "Test Back",
|
|
CreatedAt = DateTime.UtcNow
|
|
};
|
|
}
|
|
}
|
|
```
|
|
|
|
## CI/CD 整合
|
|
|
|
### GitHub Actions 設定範例
|
|
```yaml
|
|
name: Tests
|
|
on: [push, pull_request]
|
|
|
|
jobs:
|
|
test:
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- uses: actions/checkout@v2
|
|
|
|
- name: Setup .NET
|
|
uses: actions/setup-dotnet@v1
|
|
with:
|
|
dotnet-version: '8.0.x'
|
|
|
|
- name: Restore dependencies
|
|
run: dotnet restore
|
|
|
|
- name: Build
|
|
run: dotnet build --no-restore
|
|
|
|
- name: Test
|
|
run: dotnet test --no-build --verbosity normal --collect:"XPlat Code Coverage"
|
|
|
|
- name: Upload coverage reports
|
|
uses: codecov/codecov-action@v1
|
|
```
|
|
|
|
## 效能測試指南
|
|
|
|
### 基準測試
|
|
使用 BenchmarkDotNet 進行效能測試:
|
|
|
|
```csharp
|
|
[MemoryDiagnoser]
|
|
[SimpleJob(RuntimeMoniker.Net80)]
|
|
public class CachingBenchmarks
|
|
{
|
|
private ICacheService _cacheService;
|
|
|
|
[GlobalSetup]
|
|
public void Setup()
|
|
{
|
|
// 初始化快取服務
|
|
}
|
|
|
|
[Benchmark]
|
|
public async Task<string> GetFromCache()
|
|
{
|
|
return await _cacheService.GetAsync<string>("test-key");
|
|
}
|
|
}
|
|
```
|
|
|
|
## 測試最佳實踐
|
|
|
|
### DRY 原則
|
|
- 建立共用的測試基類
|
|
- 使用測試資料工廠
|
|
- 抽取共同的 Setup 邏輯
|
|
|
|
### 測試隔離
|
|
- 每個測試應該獨立執行
|
|
- 避免測試之間的依賴關係
|
|
- 使用 `IDisposable` 清理資源
|
|
|
|
### 可讀性
|
|
- 使用描述性的測試名稱
|
|
- 明確的 Arrange-Act-Assert 結構
|
|
- 適量的註解說明複雜邏輯
|
|
|
|
## 未來擴展計劃
|
|
|
|
1. **測試覆蓋率目標**: 達到 80% 以上的程式碼覆蓋率
|
|
2. **自動化測試**: 整合 CI/CD 管道
|
|
3. **效能回歸測試**: 建立效能基準測試
|
|
4. **安全性測試**: 加入安全相關的測試案例
|
|
|
|
---
|
|
|
|
**版本**: 1.0
|
|
**建立日期**: 2025-09-30
|
|
**維護者**: DramaLing 開發團隊 |