dotnet-testing-fluentvalidation-testing — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited dotnet-testing-fluentvalidation-testing (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
驗證器是應用程式的第一道防線,測試驗證器能:
<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
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 天挑戰」系列文章:
unit-test-fundamentals - 單元測試基礎nsubstitute-mocking - 測試替身與模擬~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.