diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d1c860..5563acf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## 0.9.0 + +- Added quick validation support via `QuickValidator` and its `Validate` overloads. +- Improve performance by applying options in `ExpressValidator` during the `ExpressValidatorBuilder.Build` call instead of at validation time. +- Introduce the `Unit` readonly struct. +- Add 'Nuances Of Using The Library' README Chapter. +- Add 'Nuances Of Using The Library' NuGet README Chapter. +- Add 'Quick Validation' README Chapter. +- Add 'Quick Validation' NuGet README Chapter. + + ## 0.5.0 - Introduced the `IExpressValidatorBuilder.BuildAndValidate(TObj, TOptions)` extension method. diff --git a/README.md b/README.md index ba94950..98e6dab 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ ExpressValidator is a library that provides the ability to validate objects usin - Supports adding a property or field for validation. - Verifies that a property expression is a property and a field expression is a field, and throws `ArgumentException` if it is not. - Supports adding a `Func` that provides a value for validation. +- Provides quick validation (refers to ease of use). - Supports asynchronous validation. - Targets .NET Standard 2.0+ @@ -129,6 +130,49 @@ if(!result2.IsValid) } ``` +## ⏩ Quick Validation + +Quick validation is convenient for primitive types or types without properties/fields (here, 'quick' refers to usability, not performance). Simply call `QuickValidator.Validate` on the object with a preconfigured rule: + +```csharp +var value = 5; +// result.IsValid == false +// result.Errors[0].PropertyName == "value" +var result = QuickValidator.Validate( + value, + (opt) => opt.GreaterThan(10), + nameof(value)); +``` + +For complex types, use FluentValidation's `ChildRules` method: + +```csharp +var obj = new ObjToValidate() { I = -1, PercentValue1 = 101 }; +// result.IsValid == false +// result.Errors.Count == 2 +// result.Errors[0].PropertyName == "obj.I"; result.Errors[1].PropertyName == "obj.PercentValue1" +var result = QuickValidator.Validate( + obj, + (opt) => + opt + .ChildRules((v) => v.RuleFor(o => o.I).GreaterThan(0)) + .ChildRules((v) => v.RuleFor(o => o.PercentValue1).InclusiveBetween(0, 100)), + nameof(obj)); +``` + +## 🧩 Nuances Of Using The Library + +For `ExpressValidatorBuilder` methods (`AddFunc`, `AddProperty`, and `AddField`), the overridden property name (set via `FluentValidation`'s `OverridePropertyName` method in `With(Async)Validation`) takes precedence over the property name passed as a string or via `Expression` in `AddFunc`/`AddProperty`/`AddField`. +For example, for the `ObjToValidate` object from the 'Quick Start' chapter, `result.Errors[0].PropertyName` will equal "percentSum" (the property name overridden in the validation rule): +```csharp +// result.Errors[0].PropertyName == "percentSum" +var result = new ExpressValidatorBuilder() + .AddFunc(o => o.PercentValue1 + o.PercentValue2, "sum") + .WithValidation((o) => o.InclusiveBetween(0, 100) + .OverridePropertyName("percentSum")) + .BuildAndValidate(new ObjToValidate() { PercentValue1 = 200}); +``` + ## ❌ Drawbacks - Non-canonical way of using of FluentValidation. diff --git a/src/ExpressValidator.Extensions.DependencyInjection/CHANGELOG.md b/src/ExpressValidator.Extensions.DependencyInjection/CHANGELOG.md index fefdd67..951392a 100644 --- a/src/ExpressValidator.Extensions.DependencyInjection/CHANGELOG.md +++ b/src/ExpressValidator.Extensions.DependencyInjection/CHANGELOG.md @@ -1,3 +1,12 @@ +## 0.3.5 + +- Reduced unnecessary updates to validator parameters by listening to `IOptionsMonitor.Change` with named validation options. +- Update 'ExpressValidator.Extensions.DependencyInjection' to ExpressValidator 0.5.0. +- Update Microsoft nuget packages. +- Update README with a libraries/dependencies table. +- Add a configuration section in 'ExpressValidator.Extensions.DependencyInjection.Sample' for testing purposes. + + ## 0.3.2 - Update to ExpressValidator 0.2.0. diff --git a/src/ExpressValidator.Extensions.DependencyInjection/ExpressValidator.Extensions.DependencyInjection.csproj b/src/ExpressValidator.Extensions.DependencyInjection/ExpressValidator.Extensions.DependencyInjection.csproj index fd61276..9a0dd09 100644 --- a/src/ExpressValidator.Extensions.DependencyInjection/ExpressValidator.Extensions.DependencyInjection.csproj +++ b/src/ExpressValidator.Extensions.DependencyInjection/ExpressValidator.Extensions.DependencyInjection.csproj @@ -3,7 +3,7 @@ netstandard2.0 true - 0.3.2 + 0.3.5 true Andrey Kolesnichenko MIT @@ -15,7 +15,7 @@ FluentValidation Validation DependencyInjection The ExpressValidator.Extensions.DependencyInjection package extends ExpressValidator to provide integration with Microsoft Dependency Injection. Copyright 2024 Andrey Kolesnichenko - 0.3.2.0 + 0.3.5.0 diff --git a/src/ExpressValidator/ExpressValidator.TOptions.cs b/src/ExpressValidator/ExpressValidator.TOptions.cs index a20fe71..93fc22d 100644 --- a/src/ExpressValidator/ExpressValidator.TOptions.cs +++ b/src/ExpressValidator/ExpressValidator.TOptions.cs @@ -14,15 +14,18 @@ namespace ExpressValidator /// public class ExpressValidator : IExpressValidator { - private readonly TOptions _options; private readonly IEnumerable> _validators; private readonly OnFirstPropertyValidatorFailed _validationMode; internal ExpressValidator(TOptions options, IEnumerable> validators, OnFirstPropertyValidatorFailed validationMode) { - _options = options; _validators = validators; _validationMode = validationMode; + + foreach (var validator in _validators) + { + validator.ApplyOptions(options); + } } public ValidationResult Validate(TObj obj) @@ -44,8 +47,6 @@ private async Task ValidateWithBreakAsync(TObj obj, Cancellati foreach (var validator in _validators) { token.ThrowIfCancellationRequested(); - - validator.ApplyOptions(_options); var (IsValid, Failures) = await validator.ValidateAsync(obj, token).ConfigureAwait(false); if (!IsValid) { @@ -61,8 +62,6 @@ private async Task ValidateWithContinueAsync(TObj obj, Cancell foreach (var validator in _validators) { token.ThrowIfCancellationRequested(); - - validator.ApplyOptions(_options); var (IsValid, Failures) = await validator.ValidateAsync(obj, token); if (!IsValid) { @@ -76,7 +75,6 @@ private ValidationResult ValidateWithBreak(TObj obj) { foreach (var validator in _validators) { - validator.ApplyOptions(_options); var (IsValid, Failures) = validator.Validate(obj); if (!IsValid) { @@ -91,7 +89,6 @@ private ValidationResult ValidateWithContinue(TObj obj) var currentFailures = new List(); foreach (var validator in _validators) { - validator.ApplyOptions(_options); var (IsValid, Failures) = validator.Validate(obj); if (!IsValid) { diff --git a/src/ExpressValidator/ExpressValidator.csproj b/src/ExpressValidator/ExpressValidator.csproj index 255b004..2e9f057 100644 --- a/src/ExpressValidator/ExpressValidator.csproj +++ b/src/ExpressValidator/ExpressValidator.csproj @@ -3,7 +3,7 @@ netstandard2.0 true - 0.5.0 + 0.9.0 true Andrey Kolesnichenko ExpressValidator is a library that provides the ability to validate objects using the FluentValidation library, but without object inheritance from `AbstractValidator`. @@ -15,7 +15,7 @@ ExpressValidator.png NuGet.md - 0.5.0.0 + 0.9.0.0 0.0.0.0 diff --git a/src/ExpressValidator/PropertyValidators/ExpressPropertyValidator.TOptions.cs b/src/ExpressValidator/PropertyValidators/ExpressPropertyValidator.TOptions.cs index 342f959..8a9faa0 100644 --- a/src/ExpressValidator/PropertyValidators/ExpressPropertyValidator.TOptions.cs +++ b/src/ExpressValidator/PropertyValidators/ExpressPropertyValidator.TOptions.cs @@ -11,6 +11,7 @@ internal class ExpressPropertyValidator : IExpressPropertyVal { private readonly string _propName; private readonly Func _propertyFunc; + private TypeValidatorBase _typeValidator; private Action> _actionWithOptions; @@ -30,9 +31,7 @@ public void SetValidation(Action> action) public Task<(bool IsValid, List Failures)> ValidateAsync(TObj obj, CancellationToken token = default) { - var typeValidator = new TypeAsyncValidator(); - typeValidator.SetValidation(_action, _propName); - return typeValidator.ValidateExAsync(_propertyFunc(obj), token); + return _typeValidator.ValidateExAsync(_propertyFunc(obj), token); } public (bool IsValid, List Failures) Validate(TObj obj) @@ -41,14 +40,26 @@ public void SetValidation(Action> action) { throw new InvalidOperationException(); } - var typeValidator = new TypeValidator(); - typeValidator.SetValidation(_action, _propName); - return typeValidator.ValidateEx(_propertyFunc(obj)); + return _typeValidator.ValidateEx(_propertyFunc(obj)); } public void ApplyOptions(TOptions options) { _action =_actionWithOptions.Apply(options); + SetTypeValidator(); + } + + private void SetTypeValidator() + { + if (IsAsync) + { + _typeValidator = new TypeAsyncValidator(); + } + else + { + _typeValidator = new TypeValidator(); + } + _typeValidator.SetValidation(_action, _propName); } public bool IsAsync { get; } diff --git a/src/ExpressValidator/QuickValidation/PropertyNameMode.cs b/src/ExpressValidator/QuickValidation/PropertyNameMode.cs new file mode 100644 index 0000000..30bd02f --- /dev/null +++ b/src/ExpressValidator/QuickValidation/PropertyNameMode.cs @@ -0,0 +1,18 @@ +namespace ExpressValidator.QuickValidation +{ + /// + /// Determines how the property name is set when quick validation fails. + /// + public enum PropertyNameMode + { + /// + /// Use the literal string "Input" as the property name. + /// + Default, + + /// + /// Use the type's name (typeof(T).Name) of the validated object as the property name. + /// + TypeName + } +} diff --git a/src/ExpressValidator/QuickValidation/QuickValidator.cs b/src/ExpressValidator/QuickValidation/QuickValidator.cs new file mode 100644 index 0000000..6426b7d --- /dev/null +++ b/src/ExpressValidator/QuickValidation/QuickValidator.cs @@ -0,0 +1,57 @@ +using ExpressValidator.Extensions; +using FluentValidation; +using FluentValidation.Results; +using System; + +namespace ExpressValidator.QuickValidation +{ + /// + /// Provides methods for quick validation. + /// + public static class QuickValidator + { + private const string FALLBACK_PROP_NAME = "Input"; + + /// + /// Validates the given object instance using . + /// + /// The type of the object to validate. + /// The object to validate. + /// Action to add validators. + /// The name of the property if the validation fails. + /// If , "Input" will be used. + /// Specifies a method to execute when validation succeeds. + /// + public static ValidationResult Validate(T obj, Action> action, string propName, Action onSuccessValidation = null) + { + return ValidateInner(obj, action, propName ?? FALLBACK_PROP_NAME, onSuccessValidation); + } + + /// + /// Validates the given object instance using . + /// + /// The type of the object to validate. + /// The object to validate. + /// Action to add validators. + /// . + /// If , "Input" will be used. + /// Specifies a method to execute when validation succeeds. + /// + public static ValidationResult Validate(T obj, Action> action, PropertyNameMode mode = PropertyNameMode.Default, Action onSuccessValidation = null) + { + return ValidateInner(obj, action, GetPropName(mode), onSuccessValidation); + } + + private static ValidationResult ValidateInner(T obj, Action> action, string propName, Action onSuccessValidation = null) + { + var eb = new ExpressValidatorBuilder(); + return eb.AddFunc((_) => obj, + propName, + onSuccessValidation) + .WithValidation(action) + .BuildAndValidate(Unit.Default); + } + + private static string GetPropName(PropertyNameMode mode) => mode == PropertyNameMode.Default ? FALLBACK_PROP_NAME : typeof(T).Name; + } +} diff --git a/src/ExpressValidator/Unit.cs b/src/ExpressValidator/Unit.cs new file mode 100644 index 0000000..ac6385d --- /dev/null +++ b/src/ExpressValidator/Unit.cs @@ -0,0 +1,7 @@ +namespace ExpressValidator +{ + public readonly struct Unit + { + public static readonly Unit Default = new Unit(); + } +} \ No newline at end of file diff --git a/src/ExpressValidator/ValidatorBuilders/ExpressValidatorBuilder.cs b/src/ExpressValidator/ValidatorBuilders/ExpressValidatorBuilder.cs index 9985aff..cc745c2 100644 --- a/src/ExpressValidator/ValidatorBuilders/ExpressValidatorBuilder.cs +++ b/src/ExpressValidator/ValidatorBuilders/ExpressValidatorBuilder.cs @@ -50,7 +50,7 @@ public IBuilderWithPropValidator AddField(Expression> /// A type of value. /// Func for object /// A name of the property if the validation failed. - /// Func Result Validation Success Handler + /// Specifies a method to execute when validation succeeds. /// public IBuilderWithPropValidator AddFunc(Func func, string propName, Action onSuccessValidation = null) { diff --git a/src/ExpressValidator/docs/NuGet.md b/src/ExpressValidator/docs/NuGet.md index 7bbdb60..2d7fa16 100644 --- a/src/ExpressValidator/docs/NuGet.md +++ b/src/ExpressValidator/docs/NuGet.md @@ -8,6 +8,7 @@ ExpressValidator is a library that provides the ability to validate objects usin - Supports adding a property or field for validation. - Verifies that a property expression is a property and a field expression is a field, and throws `ArgumentException` if it is not. - Supports adding a `Func` that provides a value for validation. +- Provides quick validation (refers to ease of use). - Supports asynchronous validation. - Targets .NET Standard 2.0+ @@ -51,6 +52,8 @@ if(!result.IsValid) } ``` +## Modifying FluentValidation Validator Parameters Using Options + To dynamically change the parameters of the `FluentValidation` validators: 1. Create an options object that contains the parameters of validators. @@ -111,3 +114,46 @@ if(!result2.IsValid) ... } ``` + +## Quick Validation + +Quick validation is convenient for primitive types or types without properties/fields (here, 'quick' refers to usability, not performance). Simply call `QuickValidator.Validate` on the object with a preconfigured rule: + +```csharp +var value = 5; +// result.IsValid == false +// result.Errors[0].PropertyName == "value" +var result = QuickValidator.Validate( + value, + (opt) => opt.GreaterThan(10), + nameof(value)); +``` + +For complex types, use FluentValidation's `ChildRules` method: + +```csharp +var obj = new ObjToValidate() { I = -1, PercentValue1 = 101 }; +// result.IsValid == false +// result.Errors.Count == 2 +// result.Errors[0].PropertyName == "obj.I"; result.Errors[1].PropertyName == "obj.PercentValue1" +var result = QuickValidator.Validate( + obj, + (opt) => + opt + .ChildRules((v) => v.RuleFor(o => o.I).GreaterThan(0)) + .ChildRules((v) => v.RuleFor(o => o.PercentValue1).InclusiveBetween(0, 100)), + nameof(obj)); +``` + +## Nuances Of Using The Library + +For `ExpressValidatorBuilder` methods (`AddFunc`, `AddProperty`, and `AddField`), the overridden property name (set via `FluentValidation`'s `OverridePropertyName` method in `With(Async)Validation`) takes precedence over the property name passed as a string or via `Expression` in `AddFunc`/`AddProperty`/`AddField`. +For example, for the `ObjToValidate` object from the 'Quick Start' chapter, `result.Errors[0].PropertyName` will equal "percentSum" (the property name overridden in the validation rule): +```csharp +// result.Errors[0].PropertyName == "percentSum" +var result = new ExpressValidatorBuilder() + .AddFunc(o => o.PercentValue1 + o.PercentValue2, "sum") + .WithValidation((o) => o.InclusiveBetween(0, 100) + .OverridePropertyName("percentSum")) + .BuildAndValidate(new ObjToValidate() { PercentValue1 = 200}); +``` \ No newline at end of file diff --git a/tests/ExpressValidator.Tests/ExpressValidator.Tests.csproj b/tests/ExpressValidator.Tests/ExpressValidator.Tests.csproj index bcef3cb..faee7f7 100644 --- a/tests/ExpressValidator.Tests/ExpressValidator.Tests.csproj +++ b/tests/ExpressValidator.Tests/ExpressValidator.Tests.csproj @@ -52,25 +52,22 @@ ..\..\packages\NUnit.4.3.2\lib\net462\nunit.framework.legacy.dll - - ..\..\packages\System.Buffers.4.6.0\lib\net462\System.Buffers.dll + + ..\..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll - - ..\..\packages\System.Memory.4.6.0\lib\net462\System.Memory.dll + + ..\..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll - - ..\..\packages\System.Numerics.Vectors.4.6.0\lib\net462\System.Numerics.Vectors.dll + + ..\..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll - - ..\..\packages\System.Runtime.CompilerServices.Unsafe.6.1.0\lib\net462\System.Runtime.CompilerServices.Unsafe.dll + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll - - ..\..\packages\System.Threading.Tasks.Extensions.4.6.0\lib\net462\System.Threading.Tasks.Extensions.dll - - - ..\..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + ..\..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll @@ -84,6 +81,7 @@ + @@ -115,5 +113,7 @@ + + \ No newline at end of file diff --git a/tests/ExpressValidator.Tests/QuickValidatorTests.cs b/tests/ExpressValidator.Tests/QuickValidatorTests.cs new file mode 100644 index 0000000..ee97206 --- /dev/null +++ b/tests/ExpressValidator.Tests/QuickValidatorTests.cs @@ -0,0 +1,252 @@ +using ExpressValidator.QuickValidation; +using FluentValidation; +using NUnit.Framework; +using System; + +namespace ExpressValidator.Tests +{ + internal class QuickValidatorTests + { + [Test] + public void Should_Fail_WithExpectedPropertyName_When_ValidationFails_ForPrimitiveType_UsingOverload_WithPropertyName() + { + const int valueToTest = 5; + var result = QuickValidator.Validate(valueToTest, + (opt) => opt.GreaterThan(10) + .GreaterThan(15), + nameof(valueToTest)); + Assert.That(result.IsValid, Is.False); + Assert.That(result.Errors.Count, Is.EqualTo(2)); + Assert.That(result.Errors[0].PropertyName, Is.EqualTo(nameof(valueToTest))); + } + + [Test] + public void Should_Fail_WithOverriddenPropertyName_When_ValidationFails_ForPrimitiveType_UsingOverload_WithPropertyName() + { + const int valueToTest = 5; + const string propName = "MyPropName"; + var result = QuickValidator.Validate(valueToTest, + (opt) => opt + .OverridePropertyName(propName) + .GreaterThan(10) + .GreaterThan(15), + nameof(valueToTest)); + Assert.That(result.IsValid, Is.False); + Assert.That(result.Errors.Count, Is.EqualTo(2)); + Assert.That(result.Errors[0].PropertyName, Is.EqualTo(propName)); + } + + [Test] + [TestCase(PropertyNameMode.Default)] + [TestCase(PropertyNameMode.TypeName)] + public void Should_Fail_WithOverriddenPropertyName_When_ValidationFails_ForPrimitiveType_UsingOverload_WithPropertyNameMode(PropertyNameMode mode) + { + const int valueToTest = 5; + const string propName = "MyPropName"; + var result = QuickValidator.Validate(valueToTest, + (opt) => opt + .OverridePropertyName(propName) + .GreaterThan(10) + .GreaterThan(15), + mode); + Assert.That(result.IsValid, Is.False); + Assert.That(result.Errors.Count, Is.EqualTo(2)); + Assert.That(result.Errors[0].PropertyName, Is.EqualTo(propName)); + Assert.That(result.Errors[1].PropertyName, Is.EqualTo(propName)); + } + + [Test] + [TestCase(PropertyNameMode.Default)] + [TestCase(PropertyNameMode.TypeName)] + public void Should_Fail_WithExpectedPropertyName_When_ValidationFails_ForPrimitiveType_UsingOverload_WithPropertyNameMode(PropertyNameMode mode) + { + const int valueToTest = 5; + var result = QuickValidator.Validate(valueToTest, + (opt) => opt.GreaterThan(10) + .GreaterThan(15), + mode); + Assert.That(result.IsValid, Is.False); + Assert.That(result.Errors.Count, Is.EqualTo(2)); + if (mode == PropertyNameMode.Default) + { + Assert.That(result.Errors[0].PropertyName, Is.EqualTo("Input")); + } + else + { + Assert.That(result.Errors[0].PropertyName, Is.EqualTo(typeof(int).Name)); + } + } + + [Test] + public void Should_Fail_WithExpectedPropertyName_When_ValidationFails_ForNonPrimitiveType_UsingOverload_WithPropertyName() + { + var objToQuick = new ObjWithTwoPublicProps() { I = -1, PercentValue1 = 101 }; + var rule = GetRule(); + + var result = QuickValidator.Validate(objToQuick, + rule, + nameof(objToQuick)); + + Assert.That(result.IsValid, Is.False); + Assert.That(result.Errors.Count, Is.EqualTo(2)); + Assert.That(result.Errors[0].PropertyName, Is.EqualTo(nameof(objToQuick) + "." + nameof(ObjWithTwoPublicProps.I))); + } + + [Test] + public void Should_Fail_WithOverriddenPropertyName_When_ValidationFails_ForNonPrimitiveType_UsingOverload_WithPropertyName() + { + var objToQuick = new ObjWithTwoPublicProps() { I = -1, PercentValue1 = 101 }; + var rule = GetRuleWithOverriddenPropertyName(); + + var result = QuickValidator.Validate(objToQuick, + rule, + nameof(objToQuick)); + + Assert.That(result.IsValid, Is.False); + Assert.That(result.Errors.Count, Is.EqualTo(2)); + Assert.That(result.Errors[0].PropertyName, Is.EqualTo(nameof(objToQuick) + ".MyPropNameI")); + } + + [Test] + [TestCase(PropertyNameMode.Default)] + [TestCase(PropertyNameMode.TypeName)] + public void Should_Fail_WithOverriddenPropertyName_When_ValidationFails_ForNonPrimitiveType_UsingOverload_WithPropertyNameMode(PropertyNameMode mode) + { + var objToQuick = new ObjWithTwoPublicProps() { I = -1, PercentValue1 = 101 }; + var rule = GetRuleWithOverriddenPropertyName(); + + var result = QuickValidator.Validate(objToQuick, + rule, + mode); + + Assert.That(result.IsValid, Is.False); + Assert.That(result.Errors.Count, Is.EqualTo(2)); + if (mode == PropertyNameMode.Default) + { + Assert.That(result.Errors[0].PropertyName, Is.EqualTo("Input.MyPropNameI")); + } + else + { + Assert.That(result.Errors[0].PropertyName, Is.EqualTo(nameof(ObjWithTwoPublicProps) + ".MyPropNameI")); + } + } + + [Test] + [TestCase(PropertyNameMode.Default)] + [TestCase(PropertyNameMode.TypeName)] + public void Should_Fail_WithExpectedPropertyName_When_ValidationFails_ForNonPrimitiveType_UsingOverload_WithPropertyNameMode(PropertyNameMode mode) + { + var objToQuick = new ObjWithTwoPublicProps() { I = -1, PercentValue1 = 101 }; + var rule = GetRule(); + + var result = QuickValidator.Validate(objToQuick, + rule, + mode); + + Assert.That(result.IsValid, Is.False); + Assert.That(result.Errors.Count, Is.EqualTo(2)); + if (mode == PropertyNameMode.Default) + { + Assert.That(result.Errors[0].PropertyName, Is.EqualTo("Input." + nameof(ObjWithTwoPublicProps.I))); + } + else + { + Assert.That(result.Errors[0].PropertyName, Is.EqualTo(nameof(ObjWithTwoPublicProps) + "." + nameof(ObjWithTwoPublicProps.I))); + } + } + + [Test] + public void Should_Pass_Validation_When_Valid() + { + const int valueToTest = 25; + var result = QuickValidator.Validate(valueToTest, + (opt) => opt.GreaterThan(10) + .InclusiveBetween(15, 25)); + Assert.That(result.IsValid, Is.True); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Should_Call_OnSuccess_When_Validation_Succeeds(bool isValid) + { + int valueFromHandler = 0; + int valueToTest; + if (isValid) + { + valueToTest = 25; + } + else + { + valueToTest = 5; + } + + var result = QuickValidator.Validate(valueToTest, + (opt) => opt.GreaterThan(10), + "vv", + (v) => valueFromHandler = v); + if (isValid) + { + Assert.That(result.IsValid, Is.True); + Assert.That(valueFromHandler, Is.EqualTo(25)); + } + else + { + Assert.That(result.IsValid, Is.False); + Assert.That(valueFromHandler, Is.EqualTo(0)); + } + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Should_Call_OnSuccess_When_Validation_Succeeds_UsingOverload_WithPropertyNameMode(bool isValid) + { + int valueFromHandler = 0; + int valueToTest; + if (isValid) + { + valueToTest = 25; + } + else + { + valueToTest = 5; + } + + var result = QuickValidator.Validate(valueToTest, + (opt) => opt.GreaterThan(10), + PropertyNameMode.TypeName, + (v) => valueFromHandler = v); + if (isValid) + { + Assert.That(result.IsValid, Is.True); + Assert.That(valueFromHandler, Is.EqualTo(25)); + } + else + { + Assert.That(result.IsValid, Is.False); + Assert.That(valueFromHandler, Is.EqualTo(0)); + } + } + + private static Action> GetRule() + { + return (opt) => + opt + .ChildRules((v) => v.RuleFor(o => o.I) + .GreaterThan(0)) + .ChildRules((v) => v.RuleFor(o => o.PercentValue1) + .InclusiveBetween(0, 100)); + } + + private static Action> GetRuleWithOverriddenPropertyName() + { + return (opt) => + opt + .ChildRules((v) => v.RuleFor(o => o.I) + .GreaterThan(0).OverridePropertyName("MyPropNameI")) + .ChildRules((v) => v.RuleFor(o => o.PercentValue1) + .InclusiveBetween(0, 100)); + } + } +} diff --git a/tests/ExpressValidator.Tests/app.config b/tests/ExpressValidator.Tests/app.config index d207bec..1983c0c 100644 --- a/tests/ExpressValidator.Tests/app.config +++ b/tests/ExpressValidator.Tests/app.config @@ -8,15 +8,15 @@ - + - + - + diff --git a/tests/ExpressValidator.Tests/packages.config b/tests/ExpressValidator.Tests/packages.config index b2a4321..1b3d233 100644 --- a/tests/ExpressValidator.Tests/packages.config +++ b/tests/ExpressValidator.Tests/packages.config @@ -3,10 +3,10 @@ - - - - - - + + + + + + \ No newline at end of file