Crabalidator.Testing 1.0.4

dotnet add package Crabalidator.Testing --version 1.0.4
                    
NuGet\Install-Package Crabalidator.Testing -Version 1.0.4
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Crabalidator.Testing" Version="1.0.4" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Crabalidator.Testing" Version="1.0.4" />
                    
Directory.Packages.props
<PackageReference Include="Crabalidator.Testing" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Crabalidator.Testing --version 1.0.4
                    
#r "nuget: Crabalidator.Testing, 1.0.4"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Crabalidator.Testing@1.0.4
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Crabalidator.Testing&version=1.0.4
                    
Install as a Cake Addin
#tool nuget:?package=Crabalidator.Testing&version=1.0.4
                    
Install as a Cake Tool

Crabalidator

Build NuGet License

Crabalidator is a high-performance .NET validation library built on top of DynaBee runtime code generation.

Crabalidator is designed for applications that want FluentValidation-style validators, but prefer compiled runtime validation plans over reflection-heavy execution paths. Validators describe rules through a familiar fluent API, Crabalidator turns those rules into backend-neutral validation plans, and DynaBee generates executable validation methods for hot sync paths.

What Crabalidator Does

  • Configures validators with CrabValidator<T> and RuleFor(...).
  • Supports common sync rules such as NotEmpty, string rules, collection count rules, comparisons, equality, membership, and custom Must(...) predicates.
  • Supports property-level When(...) and Unless(...) conditions.
  • Supports Cascade(CascadeMode.Stop) for fail-fast property validation.
  • Supports inferred nested object validation through ValidateNested(...).
  • Supports inferred collection element validation through ValidateEach(...).
  • Supports async custom rules through MustAsync(...).
  • Supports async nested validators and cancellation.
  • Integrates with Microsoft.Extensions.DependencyInjection.
  • Provides ICrabalidator, typed IValidator<T>, and typed IAsyncValidator<T> runtime APIs.
  • Provides registered validator diagnostics and readable validation plan output through DescribePlan(...).
  • Uses DynaBee-generated method bodies and invokers for optimized sync validation paths.
  • Optimizes custom Must(...) predicates by emitting direct DynaBee calls for visible delegate methods.
  • Includes BenchmarkDotNet coverage against FluentValidation baselines.

Design Goals

Crabalidator is intentionally split from the generation engine.

Crabalidator owns:

  • validation configuration
  • validation planning
  • rule semantics
  • diagnostics
  • dependency injection
  • runtime validator registration
  • public validation APIs

DynaBee owns:

  • generated type creation
  • generated method body creation
  • generated method invocation
  • low-level runtime code generation details

This boundary keeps Crabalidator focused on validation behavior while allowing DynaBee to evolve as a general-purpose runtime generation engine.

Requirements

  • .NET SDK 10.0+ recommended for development.
  • The library multi-targets net8.0, net9.0, and net10.0.

Installation

Install Crabalidator from NuGet:

dotnet add package Crabalidator --version 1.0.3

For local development, reference the project directly or use the solution in this repository.

Quick Start

Define a validator:

using Crabalidator;

public sealed class CustomerValidator : CrabValidator<Customer>
{
    public CustomerValidator()
    {
        RuleFor(x => x.Name)
            .Cascade(CascadeMode.Stop)
            .NotEmpty()
            .MinimumLength(3);

        RuleFor(x => x.Age)
            .GreaterThanOrEqualTo(18);

        RuleFor(x => x.Email)
            .NotEmpty()
            .Must(value => value.Contains('@'));
    }
}

public sealed class Customer
{
    public string Name { get; set; }

    public int Age { get; set; }

    public string Email { get; set; }
}

Built-In Rules

Crabalidator exposes common validators as extension methods in the base Crabalidator namespace:

RuleFor(x => x.Name)
    .NotEmpty()
    .MinimumLength(3)
    .MaximumLength(80)
    .StartsWith("CR")
    .Contains("AB")
    .Matches("^CRAB");

RuleFor(x => x.Email)
    .NotNull()
    .EmailAddress();

RuleFor(x => x.Age)
    .NotEmpty()
    .InclusiveBetween(18, 99);

RuleFor(x => x.Items)
    .NotEmpty()
    .MinimumCount(1)
    .MaximumCount(10);

RuleFor(x => x.Status)
    .In("ACTIVE", "PENDING")
    .NotIn("BLOCKED");

Custom Rules

Use Must(...) for project-specific rules:

RuleFor(x => x.Code)
    .Must(OrderRules.HasValidCode);

public static class OrderRules
{
    public static bool HasValidCode(string value)
        => value != null && value.StartsWith("CR", StringComparison.Ordinal);
}

When the predicate method is visible to generated code, Crabalidator emits a direct DynaBee call instead of going through Delegate.Invoke. Opaque lambdas and private methods still work and automatically fall back to delegate invocation.

Validate directly:

var validator = new CustomerValidator();
var result = validator.Validate(new Customer());

if (!result.IsValid)
{
    foreach (var failure in result.Errors)
    {
        Console.WriteLine($"{failure.PropertyName}: {failure.ErrorMessage}");
    }
}

Dependency Injection

Register Crabalidator with the assemblies that contain validators:

using Crabalidator;
using Crabalidator.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();

services.AddCrabalidator(typeof(CustomerValidator).Assembly);

var provider = services.BuildServiceProvider();
var validator = provider.GetRequiredService<IValidator<Customer>>();

var result = validator.Validate(customer);

Applications that prefer a single service entry point can use ICrabalidator:

var crabalidator = provider.GetRequiredService<ICrabalidator>();

ValidationResult result = crabalidator.Validate(customer);

Conditions And Cascade

Conditions apply to the property rule chain:

RuleFor(x => x.ReferralCode)
    .NotEmpty()
    .When(x => x.RequiresReferral);

Use cascade stop to avoid running later validators for the same property after the first failure:

RuleFor(x => x.Name)
    .Cascade(CascadeMode.Stop)
    .NotEmpty()
    .MinimumLength(3);

Nested Validators

Validate child objects:

ValidateNested(x => x.Address);

Validate collection elements:

RuleFor(x => x.Items)
    .NotEmpty();

ValidateEach(x => x.Items);

Nested failures are returned with prefixed paths such as Address.PostalCode or Items[0].Sku.

When more than one validator exists for a nested model, choose the validator explicitly:

ValidateNestedWith<ShippingAddressValidator>(x => x.Address);
ValidateEachWith<StrictOrderItemValidator>(x => x.Items);

Async Validation

Use MustAsync(...) for async checks:

RuleFor(x => x.Username)
    .NotEmpty()
    .MustAsync(IsUsernameAvailableAsync);

static async ValueTask<bool> IsUsernameAvailableAsync(
    string username,
    CancellationToken cancellationToken)
{
    await Task.Delay(10, cancellationToken);
    return username != "taken";
}

Validators that contain async rules must be executed with ValidateAsync(...).

Testing

Install the testing helpers from NuGet:

dotnet add package Crabalidator.Testing --version 1.0.3

Register validators in a lightweight test service collection:

using Crabalidator.Testing;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();

services.AddCrabalidatorTesting(typeof(CustomerValidator).Assembly);

var provider = services.BuildServiceProvider();
var validator = provider.GetRequiredService<IAsyncValidator<Customer>>();

var result = await validator.ValidateAsync(customer);

External test hosts can use the adapter-friendly registration:

services.AddCrabalidatorTestingAdapter(typeof(CustomerValidator).Assembly);

Run a specific validator directly without building a service provider:

var result = await CrabalidatorTest
    .For<CustomerValidator>()
    .ValidateAsync(customer);

For unit tests that already construct the validator, use TestValidate(...) directly:

var result = new CreateHeroRequestValidator()
    .TestValidate(new CreateHeroRequest("", "", "", 101, CId.Empty));

result.ShouldHaveErrorFor(request => request.Alias);
result.ShouldHaveErrorFor(request => request.PowerLevel);
result.ShouldHaveErrorFor(request => request.TeamId);

For request validation through dependency injection, use the lightweight test host:

await using var host = await CrabalidatorTestHost
    .Create()
    .UseValidatorsFromAssembly(typeof(Constants).Assembly)
    .BuildAsync();

var result = await host.ValidateAsync(request);

Assert validation results:

result.ShouldBeValid();

result.ShouldHaveErrorFor<Customer>(x => x.Email);
result.ShouldHaveErrorMessage("Email is required.");
result.ShouldHaveErrorCode("customer.email.required");

Property assertions can be scoped to a single property and chained:

result
    .ShouldHaveErrorFor<Customer>(x => x.Email)
    .WithErrorCount(2)
    .WithMessage("Email is required.")
    .WithErrorCode("customer.email.required")
    .WithSeverity(ValidationSeverity.Error);

Results returned by CrabalidatorTest.For<TValidator>() are typed, so direct validator tests can use the shorter property assertion:

var result = await CrabalidatorTest
    .For<CustomerValidator>()
    .ValidateAsync(customer);

result.ShouldHaveErrorFor(x => x.Email);

Diagnostics

Crabalidator can describe registered validators and generated validation plans:

using Crabalidator.Diagnostics;

var diagnostics = provider.GetRequiredService<ICrabalidatorDiagnostics>();

Console.WriteLine(diagnostics.DescribeRegisteredValidators());
Console.WriteLine(diagnostics.DescribePlan<Customer>());

Samples And Benchmarks

Run the basic sample:

dotnet run --project samples/Crabalidator.Samples.Basic

Run the test suite:

dotnet test

Run benchmarks:

dotnet run --project benchmarks/Crabalidator.Benchmarks -c Release

Benchmark Snapshot

These numbers come from BenchmarkDotNet on Windows 11, .NET 8.0, Release mode, using the short benchmark job in this repository. Treat them as a local comparison point rather than a universal guarantee.

Built-In Extension Rules

Scenario Crabalidator FluentValidation Speedup Crabalidator Alloc FluentValidation Alloc
Valid model 265.4 ns 643.4 ns 2.4x 48 B 672 B
Invalid model 471.0 ns 9,427.9 ns 20.0x 1,160 B 20,584 B

Custom Must(...) Predicate

Scenario Crabalidator FluentValidation Speedup Crabalidator Alloc FluentValidation Alloc
Public static predicate, valid model 9.3 ns 134.4 ns 14.4x 0 B 600 B
Public static predicate, invalid model 22.9 ns 679.7 ns 29.7x 152 B 1,856 B
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Crabalidator.Testing:

Package Downloads
TurtlePath.Testing.Integration

Integration testing wrappers that compose TurtlePath.Testing with Elysium testing adapters.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.4 90 8/11/2026
1.0.3 379 8/7/2026