Skip to content

Commit

Permalink
Merge pull request #18 from tgharold/tests-tests-tests-1
Browse files Browse the repository at this point in the history
Remove generics, add tests, address a Rider warning, refactoring
  • Loading branch information
tgharold authored Mar 10, 2020
2 parents b15ed6b + 7dc63b9 commit b5cb66d
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System.ComponentModel.DataAnnotations;
using OptionsPatternMvc.Example.Attributes;

namespace OptionsPatternMvc.Example.Settings
{
[SettingsSectionName("Example")]
public class ExampleAppSettings
{
[Required]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class RecursiveDataAnnotationValidateOptions<TOptions>
: IValidateOptions<TOptions>
where TOptions : class
{
private static readonly RecursiveDataAnnotationValidator _recursiveDataAnnotationValidator = new RecursiveDataAnnotationValidator();
private readonly RecursiveDataAnnotationValidator _recursiveDataAnnotationValidator = new RecursiveDataAnnotationValidator();

public RecursiveDataAnnotationValidateOptions(string optionsBuilderName)
{
Expand Down
5 changes: 4 additions & 1 deletion examples/OptionsPatternMvc.Example/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,8 @@
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
"AllowedHosts": "*",
"Example": {
"Name": "OptionsPatternMvc.Example A"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace RecursiveDataAnnotationsValidation.Attributes
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Enum)]
[AttributeUsage(AttributeTargets.Property)]
public class SkipRecursiveValidationAttribute : Attribute
{

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ namespace RecursiveDataAnnotationsValidation
{
public interface IRecursiveDataAnnotationValidator
{
bool TryValidateObjectRecursive<T>(
T obj,
bool TryValidateObjectRecursive(
object obj,
ValidationContext validationContext,
List<ValidationResult> validationResults
) where T : class;
);

bool TryValidateObjectRecursive<T>(
T obj,
bool TryValidateObjectRecursive(
object obj,
List<ValidationResult> validationResults,
IDictionary<object, object> validationContextItems = null
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,21 @@ namespace RecursiveDataAnnotationsValidation
{
public class RecursiveDataAnnotationValidator : IRecursiveDataAnnotationValidator
{
public bool TryValidateObjectRecursive<T>(
T obj,
public bool TryValidateObjectRecursive(
object obj, // see Note 1
ValidationContext validationContext,
List<ValidationResult> validationResults
) where T : class
)
{
return TryValidateObjectRecursive(
obj,
obj,
validationResults,
validationContext.Items
);
}

public bool TryValidateObjectRecursive<T>(
T obj,
public bool TryValidateObjectRecursive(
object obj,
List<ValidationResult> validationResults,
IDictionary<object, object> validationContextItems = null
)
Expand All @@ -40,7 +40,7 @@ private bool TryValidateObject(
object obj,
ICollection<ValidationResult> validationResults,
IDictionary<object, object> validationContextItems = null
)
)
{
return Validator.TryValidateObject(
obj,
Expand All @@ -54,8 +54,8 @@ private bool TryValidateObject(
);
}

private bool TryValidateObjectRecursive<T>(
T obj,
private bool TryValidateObjectRecursive(
object obj,
ICollection<ValidationResult> validationResults,
ISet<object> validatedObjects,
IDictionary<object, object> validationContextItems = null
Expand Down Expand Up @@ -141,5 +141,24 @@ private bool TryValidateObjectRecursive<T>(

return result;
}

/* Note 1:
*
* Background information of why we don't use ValidationContext.ObjectInstance here, even though it is tempting.
*
* https://jeffhandley.com/2009-10-17/validator
*
* It’s important to note that for cross-field validation, relying on the ObjectInstance comes with a caveat.
* It’s possible that the end user has entered a value for a property that could not be set—for instance
* specifying “ABC” for a numeric field. In cases like that, asking the instance for that numeric property
* will of course not give you the “ABC” value that the user has entered, thus the object’s other properties
* are in an indeterminate state. But even so, we’ve found that it’s extremely valuable to provide this object
* instance to the validation attributes.
*
* See also:
*
* https://github.com/dotnet/corefx/blob/8b04d0a18a49448ff7c8ee63239cd6d2a2be7e14/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationContext.cs
*
*/
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<Authors>Thomas Harold</Authors>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryUrl>https://github.com/tgharold/RecursiveDataAnnotationsValidation</RepositoryUrl>
<PackageTags>dataannotation validator</PackageTags>
<PackageTags>dataannotation validation validator</PackageTags>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using RecursiveDataAnnotationsValidation.Tests.TestModels;
using Xunit;

namespace RecursiveDataAnnotationsValidation.Tests
{
public class RecursiveDataAnnotationValidatorTests
{
/// <summary>Tests that use the method which takes a ValidationContext.</summary>
public class ValidationContextTests
{
private readonly IRecursiveDataAnnotationValidator _validator = new RecursiveDataAnnotationValidator();

[Fact]
public void Pass_all_validation()
{
var sut = new SimpleExample
{
IntegerA = 100,
StringB = "test-100",
BoolC = true,
ExampleEnumD = ExampleEnum.ValueB
};

var validationContext = new ValidationContext(sut);
var validationResults = new List<ValidationResult>();
var result = _validator.TryValidateObjectRecursive(sut, validationContext, validationResults);

Assert.True(result);
Assert.Empty(validationResults);
}

[Fact]
public void Indicate_that_IntegerA_is_missing()
{
var sut = new SimpleExample
{
IntegerA = null,
StringB = "test-101",
BoolC = false,
ExampleEnumD = ExampleEnum.ValueC
};

const string fieldName = nameof(SimpleExample.IntegerA);
var validationContext = new ValidationContext(sut);
var validationResults = new List<ValidationResult>();
var result = _validator.TryValidateObjectRecursive(sut, validationContext, validationResults);

Assert.False(result);
Assert.NotEmpty(validationResults);
Assert.NotNull(validationResults
.FirstOrDefault(x => x.MemberNames.Contains(fieldName)));
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using RecursiveDataAnnotationsValidation.Tests.TestModels;
using Xunit;

Expand Down Expand Up @@ -33,5 +34,56 @@ public void Passes_all_validation()
Assert.True(result);
Assert.Empty(validationResults);
}

[Fact]
public void Fails_for_SimpleA_BoolC()
{
var sut = new SkippedChildrenExample
{
Name = "Skipped-Children-2",
SimpleA = new SimpleExample
{
IntegerA = 75124,
BoolC = null, // set one of the props to null
StringB = "simple-a-child-2",
ExampleEnumD = ExampleEnum.ValueC
},
SimpleB = new SimpleExample
{
BoolC = true
}
};
var validationResults = new List<ValidationResult>();
var result = _validator.TryValidateObjectRecursive(sut, validationResults);

Assert.False(result);
Assert.NotEmpty(validationResults);
Assert.NotNull(validationResults
.FirstOrDefault(x => x.MemberNames.Contains("SimpleA.BoolC")));
}

[Fact]
public void Fails_for_SimpleB_missing()
{
var sut = new SkippedChildrenExample
{
Name = "Skipped-Children-2",
SimpleA = new SimpleExample
{
IntegerA = 75124,
BoolC = null,
StringB = "simple-a-child-2",
ExampleEnumD = ExampleEnum.ValueC
},
SimpleB = null // the object is missing entirely
};
var validationResults = new List<ValidationResult>();
var result = _validator.TryValidateObjectRecursive(sut, validationResults);

Assert.False(result);
Assert.NotEmpty(validationResults);
Assert.NotNull(validationResults
.FirstOrDefault(x => x.MemberNames.Contains("SimpleB")));
}
}
}

0 comments on commit b5cb66d

Please sign in to comment.