Summary
- 測試 FluentValidation 驗證器的專門技能。當需要為 Validator 類別建立測試、驗證業務規則、測試錯誤訊息時使用。涵蓋 FluentValidation.TestHelper 完整使用、ShouldHaveValidationErrorFor、非同步驗證、跨欄位邏輯等。
- Make sure to…
kevintsengtw/dotnet-testing-agent-skills
測試 FluentValidation 驗證器的專門技能。當需要為 Validator 類別建立測試、驗證業務規則、測試錯誤訊息時使用。涵蓋 FluentValidation.TestHelper 完整使用、ShouldHaveValidationErrorFor、非同步驗證、跨欄位邏輯等。 Make sure to use this skill whenever the user mentions FluentValidation testing, Validator testing, ShouldHaveValidationErrorFor, TestHelper, or testing business validation rules, even if they don't explicitly ask for validation testing guidance.
npx skills add kevintsengtw/dotnet-testing-agent-skills --skill dotnet-testing-fluentvalidation-testing
Related neighbors and high-traction skills in the same topics — useful to compare before installing.
Browser automation CLI for AI agents. Use when the user needs to interact with websites, includ…
810.4K installsConfigure Azure API Management as an AI Gateway for AI models, MCP tools, and agents. WHEN: sem…
566.3K installsAzure VM/VMSS router. WHEN: create / provision / deploy / spin-up VM, recommend VM size, compar…
510K installsPostgres best practices maintained by Supabase, for Postgres running anywhere. Load this skill …
391.6K installsPrisma ORM CLI commands reference covering init, generate, migrate, db, dev, complete, studio, …
267K installsUse when doing ANY task involving Supabase. Triggers: Supabase products (Database, Auth, Edge F…
263K installsOther skills from kevintsengtw/dotnet-testing-agent-skills · top by installs.
npx skills add kevintsengtw/dotnet-testing-agent-skills
Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.
Alternate registries and mirrors of this skill.
npx skills add smithery/kevintsengtw --skill dotnet-testing-fluentvalidation-testing
main
Files included with this skill beyond the listing page.
SKILL.md
11,561 B
SUMMARY.md
811 B
驗證器是應用程式的第一道防線,測試驗證器能:
<PackageReference Include="FluentValidation" Version="12.1.1" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="Microsoft.Extensions.TimeProvider.Testing" Version="10.4.0" />
<PackageReference Include="NSubstitute" Version="5.3.0" />
<PackageReference Include="AwesomeAssertions" Version="9.4.0" />
注意:
FluentValidation.TestHelper命名空間(TestValidate、ShouldHaveValidationErrorFor等 API)已包含在FluentValidation主套件中,不需要額外安裝獨立套件。只需using FluentValidation.TestHelper;即可使用。
FluentValidation 12.x 注意事項:FluentValidation 12.0 為主要版本升級,最低需求為 .NET 8。已移除的 API 包括
Transform/TransformForEach(改用Must+ 手動轉換)、InjectValidator(改用建構子注入 +SetValidator)、CascadeMode.StopOnFirstFailure(改用RuleLevelCascadeMode = CascadeMode.Stop)。ShouldHaveAnyValidationError已更名為ShouldHaveValidationErrors。完整遷移指南請參閱 FluentValidation 12.0 Upgrade Guide。
using FluentValidation;
using FluentValidation.TestHelper;
using Microsoft.Extensions.Time.Testing;
using NSubstitute;
using Xunit;
using AwesomeAssertions;
本節涵蓋 7 種核心測試模式,每種模式包含驗證器定義與完整測試範例。
完整程式碼範例請參考 [references/core-test-patterns.md](references/core-test-patterns.md)
TestValidate + ShouldHaveValidationErrorFor / ShouldNotHaveValidationErrorFor 測試單一欄位規則[Theory] + [InlineData] 測試多種無效/有效輸入組合Must() 規則等多欄位關聯驗證TimeProvider,搭配 FakeTimeProvider 控制時間進行測試.When() 的可選欄位驗證,測試條件觸發與跳過情境MustAsync + TestValidateAsync,搭配 NSubstitute Mock 外部服務public class UserValidatorTests
{
private readonly UserValidator _validator = new();
[Fact]
public void Validate_空白使用者名稱_應該驗證失敗()
{
var result = _validator.TestValidate(
new UserRegistrationRequest { Username = "" });
result.ShouldHaveValidationErrorFor(x => x.Username)
.WithErrorMessage("使用者名稱不可為 null 或空白");
}
}
| 方法 | 用途 | 範例 |
|---|---|---|
TestValidate(model) |
執行同步驗證 | _validator.TestValidate(request) |
TestValidateAsync(model) |
執行非同步驗證 | await _validator.TestValidateAsync(request) |
| 方法 | 用途 | 範例 |
|---|---|---|
ShouldHaveValidationErrorFor(x => x.Property) |
斷言該屬性應該有錯誤 | result.ShouldHaveValidationErrorFor(x => x.Username) |
ShouldNotHaveValidationErrorFor(x => x.Property) |
斷言該屬性不應該有錯誤 | result.ShouldNotHaveValidationErrorFor(x => x.Email) |
ShouldNotHaveAnyValidationErrors() |
斷言整個物件沒有任何錯誤 | result.ShouldNotHaveAnyValidationErrors() |
| 方法 | 用途 | 範例 |
|---|---|---|
WithErrorMessage(string) |
驗證錯誤訊息內容 | .WithErrorMessage("使用者名稱不可為空") |
WithErrorCode(string) |
驗證錯誤代碼 | .WithErrorCode("NOT_EMPTY") |
方法情境預期結果 格式[Theory]
[InlineData("", "電子郵件不可為 null 或空白")]
[InlineData("invalid", "電子郵件格式不正確")]
[InlineData("@example.com", "電子郵件格式不正確")]
public void Validate_無效Email_應該驗證失敗(string email, string expectedError)
{
var request = new UserRegistrationRequest { Email = email };
var result = _validator.TestValidate(request);
result.ShouldHaveValidationErrorFor(x => x.Email).WithErrorMessage(expectedError);
}
[Theory]
[InlineData(17, "年齡必須大於或等於 18 歲")]
[InlineData(121, "年齡必須小於或等於 120 歲")]
public void Validate_無效年齡_應該驗證失敗(int age, string expectedError)
{
var request = new UserRegistrationRequest { Age = age };
var result = _validator.TestValidate(request);
result.ShouldHaveValidationErrorFor(x => x.Age).WithErrorMessage(expectedError);
}
[Fact]
public void Validate_未同意條款_應該驗證失敗()
{
var request = new UserRegistrationRequest { AgreeToTerms = false };
var result = _validator.TestValidate(request);
result.ShouldHaveValidationErrorFor(x => x.AgreeToTerms)
.WithErrorMessage("必須同意使用條款");
}
public static class TestDataBuilder
{
public static UserRegistrationRequest CreateValidRequest()
{
return new UserRegistrationRequest
{
Username = "testuser123",
Email = "[email protected]",
Password = "TestPass123",
ConfirmPassword = "TestPass123",
BirthDate = new DateTime(1990, 1, 1),
Age = 34,
PhoneNumber = "0912345678",
Roles = new List<string> { "User" },
AgreeToTerms = true
};
}
public static UserRegistrationRequest WithUsername(this UserRegistrationRequest request, string username)
{
request.Username = username;
return request;
}
public static UserRegistrationRequest WithEmail(this UserRegistrationRequest request, string email)
{
request.Email = email;
return request;
}
}
// 使用範例
var request = TestDataBuilder.CreateValidRequest()
.WithUsername("newuser")
.WithEmail("[email protected]");
此技能可與以下技能組合使用:
A: 使用 Mock 隔離資料庫依賴:
_mockUserService.IsUsernameAvailableAsync("username")
.Returns(Task.FromResult(false));
A: 使用 FakeTimeProvider 控制時間:
_fakeTimeProvider.SetUtcNow(new DateTime(2024, 1, 1));
A: 分別測試每個條件,確保完整覆蓋:
// 測試生日已過的情況
// 測試生日未到的情況
// 測試邊界日期
A: 重點測試:
本技能提供以下範本檔案:
templates/validator-test-template.cs: 完整的驗證器測試範例templates/async-validator-examples.cs: 非同步驗證範例本技能內容提煉自「老派軟體工程師的測試修練 - 30 天挑戰」系列文章:
- 鐵人賽文章:https://ithelp.ithome.com.tw/articles/10376147 - 範例程式碼:https://github.com/kevintsengtw/30DaysinTesting_Samples/tree/main/day18
unit-test-fundamentals - 單元測試基礎nsubstitute-mocking - 測試替身與模擬