本文へ移動
cccskills
無料GitHub で公開

dotnet-testing-fluentvalidation-testing

Specialized skill for testing FluentValidation validators. Use when you need to create tests for Validator classes, validate business rules, or test error messages. Covers complete FluentValidation.TestHelper usage, ShouldHaveValidationErrorFor, async validation, cross-field logic, etc. Keywords: validator, validation, fluentvalidation, validation testing, UserValidator, CreateOrderValidator, TestHelper, ShouldHaveValidationErrorFor, ShouldNotHaveValidationErrorFor, TestValidate, TestValidateAsync, testing validators, validating business rules

インストール方法を見る

含まれるファイル(1)

  • SKILL.md11.1 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Source: kevintsengtw/dotnet-testing-agent-skills (MIT). Ported into dotnet-agent-harness.

FluentValidation Validator Testing Guide

Applicable Scenarios

This skill focuses on testing data validation logic using FluentValidation.TestHelper, covering basic validation, complex business rules, async validation, and testing best practices.

Why Test Validators?

Validators are the first line of defense for applications, testing validators can:

  1. Ensure Data Integrity - Prevent invalid data from entering the system
  2. Document Business Rules - Tests serve as living documentation, clearly showing business rules
  3. Security Protection - Prevent malicious or inappropriate data input
  4. Refactoring Safety Net - Provide protection when business rules change
  5. Cross-Field Logic Validation - Ensure complex logic works correctly

Prerequisites

Package Installation

<PackageReference Include="FluentValidation" Version="11.11.0" />
<PackageReference Include="FluentValidation.TestHelper" Version="11.11.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="Microsoft.Extensions.Time.Testing" Version="9.0.0" />
<PackageReference Include="NSubstitute" Version="5.3.0" />
<PackageReference Include="AwesomeAssertions" Version="9.1.0" />

Basic using Directives

using FluentValidation;
using FluentValidation.TestHelper;
using Microsoft.Extensions.Time.Testing;
using NSubstitute;
using Xunit;
using AwesomeAssertions;

Core Testing Patterns

This section covers 7 core testing patterns, each including validator definitions and complete test examples.

For complete code examples, see references/core-test-patterns.md

  • Pattern 1: Basic Field Validation — Using TestValidate + ShouldHaveValidationErrorFor / ShouldNotHaveValidationErrorFor to test single field rules
  • Pattern 2: Parameterized Tests — Using [Theory] + [InlineData] to test multiple invalid/valid input combinations
  • Pattern 3: Cross-Field Validation — Password confirmation, custom Must() rules, and other multi-field related validations
  • Pattern 4: Time-Dependent Validation — Injecting TimeProvider, using FakeTimeProvider to control time for testing
  • Pattern 5: Conditional Validation — Using .When() for optional field validation, testing conditional trigger and skip scenarios
  • Pattern 6: Async Validation — MustAsync + TestValidateAsync, using NSubstitute Mock for external services
  • Pattern 7: Collection Validation — Validating collections non-empty and element validity

Quick Example: Basic Field Validation

public class UserValidatorTests
{
    private readonly UserValidator _validator = new();

    [Fact]
    public void Validate_Blank_Username_Should_Validation_Fail()
    {
        var result = _validator.TestValidate(
            new UserRegistrationRequest { Username = "" });

        result.ShouldHaveValidationErrorFor(x => x.Username)
              .WithErrorMessage("Username cannot be null or blank");
    }
}

FluentValidation.TestHelper Core API

Test Methods

MethodPurposeExample
TestValidate(model)Execute sync validation_validator.TestValidate(request)
TestValidateAsync(model)Execute async validationawait _validator.TestValidateAsync(request)

Assertion Methods

MethodPurposeExample
ShouldHaveValidationErrorFor(x => x.Property)Assert property should have errorresult.ShouldHaveValidationErrorFor(x => x.Username)
ShouldNotHaveValidationErrorFor(x => x.Property)Assert property should not have errorresult.ShouldNotHaveValidationErrorFor(x => x.Email)
ShouldNotHaveAnyValidationErrors()Assert entire object has no errorsresult.ShouldNotHaveAnyValidationErrors()

Error Message Validation

MethodPurposeExample
WithErrorMessage(string)Validate error message content.WithErrorMessage("Username cannot be empty")
WithErrorCode(string)Validate error code.WithErrorCode("NOT_EMPTY")

Testing Best Practices

✅ Recommended Practices

  1. Use Parameterized Tests - Use Theory to test multiple input combinations
  2. Test Boundary Values - Pay special attention to boundary conditions
  3. Control Time - Use FakeTimeProvider for time-dependent scenarios
  4. Mock External Dependencies - Use NSubstitute to isolate external services
  5. Create Helper Methods - Uniformly manage test data
  6. Clear Test Naming - Use Method_Scenario_ExpectedResult format
  7. Test Error Messages - Ensure users see correct error messages

❌ Practices to Avoid

  1. Avoid Using DateTime.Now - Causes unstable tests
  2. Avoid Overly Coupled Tests - Each test should only validate one rule
  3. Avoid Hardcoded Test Data - Use helper methods to create
  4. Avoid Ignoring Boundary Conditions - Boundary values are where errors most easily occur
  5. Avoid Skipping Error Message Validation - Error messages are part of user experience

Common Testing Scenarios

Scenario 1: Email Format Validation

[Theory]
[InlineData("", "Email cannot be null or blank")]
[InlineData("invalid", "Email format is incorrect")]
[InlineData("@example.com", "Email format is incorrect")]
public void Validate_Invalid_Email_Should_Validation_Fail(string email, string expectedError)
{
    var request = new UserRegistrationRequest { Email = email };
    var result = _validator.TestValidate(request);
    result.ShouldHaveValidationErrorFor(x => x.Email).WithErrorMessage(expectedError);
}

Scenario 2: Age Range Validation

[Theory]
[InlineData(17, "Age must be greater than or equal to 18")]
[InlineData(121, "Age must be less than or equal to 120")]
public void Validate_Invalid_Age_Should_Validation_Fail(int age, string expectedError)
{
    var request = new UserRegistrationRequest { Age = age };
    var result = _validator.TestValidate(request);
    result.ShouldHaveValidationErrorFor(x => x.Age).WithErrorMessage(expectedError);
}

Scenario 3: Required Field Validation

[Fact]
public void Validate_Not_Agree_To_Terms_Should_Validation_Fail()
{
    var request = new UserRegistrationRequest { AgreeToTerms = false };
    var result = _validator.TestValidate(request);
    result.ShouldHaveValidationErrorFor(x => x.AgreeToTerms)
          .WithErrorMessage("Must agree to terms of use");
}

Testing Helper Tools

Test Data Builder

public static class TestDataBuilder
{
    public static UserRegistrationRequest CreateValidRequest()
    {
         return new UserRegistrationRequest
         {
             Username = "testuser123",
             Email = "test@example.com",
             Password = "<DB_PASSWORD_PLACEHOLDER>",
             ConfirmPassword = "<DB_PASSWORD_PLACEHOLDER>",
             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;
    }
}

// Usage example
var request = TestDataBuilder.CreateValidRequest()
                            .WithUsername("newuser")
                            .WithEmail("new@example.com");

Integration with Other Skills

This skill can be combined with:

  • unit-test-fundamentals: Unit testing basics and 3A pattern
  • test-naming-conventions: Test naming conventions
  • nsubstitute-mocking: Mocking external service dependencies
  • test-data-builder-pattern: Building complex test data
  • datetime-testing-timeprovider: Time-dependent testing

Troubleshooting

Q1: How to Test Validators Requiring Database Queries?

A: Use Mock to isolate database dependencies:

_mockUserService.IsUsernameAvailableAsync("username")
                .Returns(Task.FromResult(false));

Q2: How to Handle Time-Related Validation?

A: Use FakeTimeProvider to control time:

_fakeTimeProvider.SetUtcNow(new DateTime(2024, 1, 1));

Q3: How to Test Complex Cross-Field Validation?

A: Test each condition separately, ensuring complete coverage:

// Test birthday passed scenario
// Test birthday not yet arrived scenario
// Test boundary date

Q4: How Much Should Be Tested?

A: Focus on testing:

  • At least one test for each validation rule
  • Boundary values and special cases
  • Error message correctness
  • All combinations of cross-field logic

Template Files Reference

This skill provides the following template files:

  • templates/validator-test-template.cs: Complete validator test example
  • templates/async-validator-examples.cs: Async validation examples

Reference Resources

Original Articles

This skill content is distilled from the "Old School Software Engineer's Testing Practice - 30 Day Challenge" article series:

Official Documentation

Related Skills

  • unit-test-fundamentals - Unit testing basics
  • nsubstitute-mocking - Test doubles and mocking

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Patterns and techniques for evaluating and improving AI agent outputs.

日本語の概要は準備中です。原文の説明を表示しています。

rudironsoni/Synaxis22026年3月17日 更新

Comprehensive AI prompt engineering safety review and improvement prompt. Analyzes prompts for safety, bias, security vulnerabilities, and effectiveness while providing detailed improvement recommendations.

日本語の概要は準備中です。原文の説明を表示しています。

rudironsoni/Synaxis22026年3月17日 更新

Use when user requests research requiring multiple sources, comprehensive analysis, or synthesis across topics - technical research, domain knowledge gathering, market analysis, or learning about complex subjects

日本語の概要は準備中です。原文の説明を表示しています。

rudironsoni/Synaxis22026年3月17日 更新

deep-wiki

無料

AI-powered wiki generation for code repositories with commands, agents, and skills

日本語の概要は準備中です。原文の説明を表示しています。

rudironsoni/Synaxis22026年3月17日 更新

Use when building .NET 10 or C# 14 applications; when using minimal APIs, modular monolith patterns, or feature folders; when implementing HTTP resilience, Options pattern, Channels, or validation; when seeing outdated patterns like old extension method syntax

日本語の概要は準備中です。原文の説明を表示しています。

rudironsoni/Synaxis22026年3月17日 更新

Implements accessible .NET UI. SemanticProperties, ARIA, AutomationPeer, testing per platform.

日本語の概要は準備中です。原文の説明を表示しています。

rudironsoni/Synaxis22026年3月17日 更新

rudironsoni のスキルをすべて見る

このスキルの問題を報告する