當我指定某個檔案需要寫單測時,你會協助我根據 BDD(行為驅動開發)原則撰寫單元測試,確保測試涵蓋所有關鍵行為和邊界條件。
querySelector、querySelectorAll、container.querySelector 或其他 CSS selector 抓取 class、DOM 節點來驗證畫面querySelector、querySelectorAll、container.querySelector,以及以 .className、[class*=...] 等 CSS selector 進行驗證。getByRole、getByLabelText、getByText。根據目標檔案的副檔名判別語言,分兩條路徑處理:
| 副檔名 | 語言 | 載入範例 |
|---|---|---|
.ts, .tsx |
TypeScript | references/typescript-example.test.ts |
.js, .jsx |
JavaScript | references/typescript-example.test.ts |
.cs |
C# | references/csharp-example-test.cs |
.java |
Java | references/java-example-test.java |
.py |
Python | references/python-example-test.py |
.dart |
Flutter/Dart | references/flutter-example.dart |
執行步驟:
副檔名不在上表時,直接以 LLM 通用知識處理:
.dart → flutter_test、.rb → RSpec、.go → testing 套件)Given [前置條件/初始狀態]
When [執行的動作/觸發事件]
Then [預期結果/驗證行為]
| 分類 | 說明 | 範例 |
|---|---|---|
| ✅ Happy Path | 正常流程、預期輸入 | 有效參數、正確格式 |
| ⚠️ Edge Cases | 邊界條件 | 空值、最大/最小值、零 |
| ❌ Error Cases | 異常處理 | 無效輸入、例外拋出 |
| 🔄 State Changes | 狀態轉換 | 初始化、重置、更新 |
採用 Given條件_When動作_Should預期行為 格式:
GivenNoItems_WhenGetList_ShouldReturnEmptyList
GivenNullInput_WhenValidate_ShouldThrowException
GivenItemsExist_WhenCalculateTotal_ShouldReturnCorrectSum
| 項目 | 規範 |
|---|---|
| 框架 | Jest |
| 檔案命名 | [ComponentName].test.tsx |
| 輸出位置 | 同目錄的 __tests__/ 資料夾 |
| Mock 工具 | jest.mock() |
| 斷言風格 | expect(result).toBe(expected) |
結構範例:
describe('模組/元件名稱', () => {
describe('方法名稱', () => {
it('Given[條件]_When[動作]_Should[預期行為]', () => {
// Given - 準備測試資料
const input = { ... };
// When - 執行被測方法
const result = targetMethod(input);
// Then - 驗證結果
expect(result).toEqual(expected);
});
});
});
| 項目 | 規範 |
|---|---|
| 框架 | xUnit |
| 檔案命名 | [ClassName]Test.cs |
| 輸出位置 | 對應的 .Tests 專案資料夾 |
| Mock 工具 | Moq |
| 斷言風格 | Assert.Equal(expected, actual) |
結構範例:
public class UserServiceTests
{
[Fact]
public void GivenUserExists_WhenGetUser_ShouldReturnUser()
{
// Given
var mockRepo = new Mock<IUserRepository>();
mockRepo.Setup(r => r.GetById(1)).Returns(new User { Id = 1 });
var service = new UserService(mockRepo.Object);
// When
var result = service.GetUser(1);
// Then
Assert.NotNull(result);
Assert.Equal(1, result.Id);
}
}
| 項目 | 規範 |
|---|---|
| 框架 | JUnit 5 / TestNG |
| 檔案命名 | [ClassName]Test.java |
| 輸出位置 | src/test/java/ 對應套件路徑 |
| Mock 工具 | Mockito, MockK (Kotlin) |
| 斷言風格 | AssertJ: assertThat(result).isEqualTo(expected) |
結構範例:
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock
private OrderRepository orderRepository;
@InjectMocks
private OrderService orderService;
@Test
@DisplayName("應該在訂單存在時返回訂單")
void GivenOrderExists_WhenGetOrder_ShouldReturnOrder() {
// Given - 設定 mock 行為
Order expected = new Order(1L, "item");
when(orderRepository.findById(1L)).thenReturn(Optional.of(expected));
// When - 執行被測方法
Order result = orderService.getOrder(1L);
// Then - 驗證結果
assertThat(result).isNotNull();
assertThat(result.getId()).isEqualTo(1L);
}
}
| 項目 | 規範 |
|---|---|
| 框架 | pytest / unittest |
| 檔案命名 | test_[module_name].py |
| 輸出位置 | tests/ 資料夾,保持與 src 相同結構 |
| Mock 工具 | unittest.mock, pytest-mock |
| 斷言風格 | assert result == expected |
結構範例:
import pytest
from unittest.mock import Mock, patch
class TestUserService:
"""UserService 單元測試"""
def test_GivenUserExists_WhenGetUser_ShouldReturnUserData(self):
"""應該在用戶存在時返回用戶資料"""
# Given
mock_repo = Mock()
mock_repo.get_by_id.return_value = {"id": 1, "name": "Test"}
service = UserService(mock_repo)
# When
result = service.get_user(1)
# Then
assert result is not None
assert result["id"] == 1
| 項目 | 規範 |
|---|---|
| 框架 | flutter_test + mocktail |
| 檔案命名 | [class_name]_test.dart |
| 輸出位置 | test/ 資料夾,保持與 lib 相同結構 |
| Mock 工具 | mocktail(class MockX extends Mock implements X {}) |
| 斷言風格 | expect(result, equals(expected)) |
結構範例:
void main() {
late CartService cartService;
late MockProductRepository mockProductRepo;
setUp(() {
mockProductRepo = MockProductRepository();
cartService = CartService(mockProductRepo);
});
group('CartService', () {
group('addItem', () {
test('GivenProductExists_WhenAddItem_ShouldAddToCart', () {
// Given
when(() => mockProductRepo.findById(1)).thenReturn(product);
// When
final result = cartService.addItem(1, 2);
// Then
expect(result.items, hasLength(1));
});
});
});
}
執行此 skill 時,請依序輸出:
## 📋 測試場景分析
**目標檔案:** `path/to/file.ts`, `path/to/other.ts`(單檔時僅列一個)
**識別的公開方法:** methodA, methodB, methodC
### 測試場景列表
| # | 方法 | 場景類型 | 描述 |
|---|------|----------|------|
| 1 | methodA | ✅ Happy | 當輸入有效時應返回正確結果 |
| 2 | methodA | ⚠️ Edge | 當輸入為空時應返回空陣列 |
| 3 | methodA | ❌ Error | 當輸入為 null 時應拋出例外 |
確認以上場景後,我將開始撰寫測試。
依據確認的場景,輸出完整測試檔案,包含:
根據判別的語言,讀取對應的範例檔案:
references/typescript-example.test.tsreferences/csharp-example-test.csreferences/java-example-test.javareferences/python-example-test.pyreferences/flutter-example.dart注意:撰寫測試前必須先讀取對應語言的範例,確保遵循一致的風格與結構。