본문으로 건너뛰기
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

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

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의 스킬 모두 보기

이 스킬의 문제 신고