From zenbu-powers
當在 Gherkin 測試中進行「Aggregate 初始狀態建立」,「只能」使用此指令。 使用 EF Core DbContext 直接寫入 DB 建立測試資料。
npx claudepluginhub zenbuapps/zenbu-powers --plugin zenbu-powersThis skill uses the workspace's default tool permissions.
使用 EF Core DbContext 直接寫入資料庫,建立測試所需的初始 Aggregate 狀態。
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Checks Next.js compilation errors using a running Turbopack dev server after code edits. Fixes actionable issues before reporting complete. Replaces `next build`.
Guides code writing, review, and refactoring with Karpathy-inspired rules to avoid overcomplication, ensure simplicity, surgical changes, and verifiable success criteria.
Share bugs, ideas, or general feedback.
使用 EF Core DbContext 直接寫入資料庫,建立測試所需的初始 Aggregate 狀態。
| 項目 | 技術 |
|---|---|
| Language | C# 12 / .NET 8+ |
| BDD | SpecFlow 3.9+(Cucumber Expressions) |
| HTTP Client | WebApplicationFactory<Program> + HttpClient |
| ORM | Entity Framework Core 8+ |
| Database | PostgreSQL 16(Testcontainers for .NET) |
| DI | Microsoft.Extensions.DependencyInjection(BoDi for SpecFlow) |
| Assertion | FluentAssertions 6+ |
| JSON | System.Text.Json |
| Auth | JWT(System.IdentityModel.Tokens.Jwt) |
| Test Runner | xUnit |
| Test Command | dotnet test --filter "Category!=Ignore" |
| Red Failure | HTTP 404 Not Found |
Given 語句描述 Aggregate 的存在狀態,即定義 Aggregate 的屬性值。
識別規則:
通用判斷:如果 Given 是在建立測試的初始資料狀態(而非執行動作),就使用此 Handler。
| aggregate-given | command(Given 用法) | |
|---|---|---|
| 目的 | 直接建立 DB 資料(繞過 API) | 透過 API 執行命令 |
| 層級 | DbContext 層(EF Core) | HTTP API 層 |
| 適用時機 | 純前置資料設定 | 測試需要經過完整 API 流程 |
ScenarioContext 取得 AppDbContext(_ctx.Get<AppDbContext>("DbContext"))dbContext.Add() 寫入dbContext.SaveChanges() 持久化Ids 字典(Ids["{natural_key}"] = entity.Id)[Binding]
public class AggregateGivenSteps
{
private readonly ScenarioContext _ctx;
private readonly HttpClient _client;
private readonly AppDbContext _dbContext;
private readonly JwtHelper _jwtHelper;
public AggregateGivenSteps(ScenarioContext ctx)
{
_ctx = ctx;
_client = ctx.Get<HttpClient>("HttpClient");
_dbContext = ctx.Get<AppDbContext>("DbContext");
_jwtHelper = ctx.Get<JwtHelper>("JwtHelper");
}
private Dictionary<string, object> Ids => _ctx.Get<Dictionary<string, object>>("Ids");
}
[Given(@"用戶 ""(.*)"" 的購物車中商品 ""(.*)"" 的數量為 (.*)")]
public void GivenCartItem(string userName, string productId, int quantity)
{
var userId = Ids[userName].ToString()!;
var dbContext = _ctx.Get<AppDbContext>("DbContext");
var cartItem = new CartItem
{
UserId = userId,
ProductId = productId,
Quantity = quantity
};
dbContext.CartItems.Add(cartItem);
dbContext.SaveChanges();
}
使用 SpecFlow 的 Table 參數搭配 table.Rows 迭代,一次建立多筆資料。
[Given(@"系統中有以下用戶:")]
public void GivenUsersExist(Table table)
{
var dbContext = _ctx.Get<AppDbContext>("DbContext");
foreach (var row in table.Rows)
{
var user = new User
{
Id = row["userId"],
Name = row["name"],
Email = row["email"]
};
dbContext.Users.Add(user);
Ids[row["name"]] = user.Id;
}
dbContext.SaveChanges();
}
[Given(@"用戶 ""(.*)"" 的個人簡介為:")]
public void GivenUserBio(string userName, string docString)
{
var userId = Ids[userName].ToString()!;
var dbContext = _ctx.Get<AppDbContext>("DbContext");
var profile = dbContext.UserProfiles.First(p => p.UserId == userId);
profile.Bio = docString;
dbContext.UserProfiles.Update(profile);
dbContext.SaveChanges();
}
從 Gherkin 的關係詞推斷 DBML 中的複合主鍵結構:
| 關係詞 | Gherkin 範例 | 複合 Key |
|---|---|---|
| 在 | 用戶 "Alice" 在課程 1 | (UserId, LessonId) |
| 對 | 用戶 "Alice" 對訂單 "ORDER-123" | (UserId, OrderId) |
| 與 | 用戶 "Alice" 與用戶 "Bob" | (UserIdA, UserIdB) |
| 於 | 商品 "MacBook" 於商店 "台北店" | (ProductId, StoreId) |
Gherkin 中的中文業務術語需映射到 DBML enum 值:
| 中文 | Enum | 情境 |
|---|---|---|
| 進行中 | InProgress | 進度、狀態 |
| 已完成 | Completed | 進度、狀態 |
| 未開始 | NotStarted | 進度、狀態 |
| 已付款 | Paid | 訂單 |
| 待付款 | Pending | 訂單 |
建議使用 Dictionary 集中管理映射:
private static readonly Dictionary<string, string> StatusMap = new()
{
["進行中"] = "InProgress",
["已完成"] = "Completed",
["未開始"] = "NotStarted",
["已付款"] = "Paid",
["待付款"] = "Pending"
};
erm.dbml 定義dbContext.Set<T>().Add() 或 dbContext.Add(),不可使用 raw SQLSaveChanges() 拋出 DbUpdateExceptionIds[naturalKey] = entity.Id,供後續 Command/Query/Then handler 使用Ids 字典Update() 避免重複插入失敗