Reputation: 1170
Is it possible to write unit tests for fluentvalidation classes when the object we are validating has child classes that are also being validated.
As an example: My class looks like this
public class TestModel
{
public class SubData
{
public int SubDataVal { get; set; }
}
public int ParentVal { get; set; }
public SubData Sub { get; set; }
}
My validation logic looks like this:
public class TestModelValidator : AbstractValidator<TestModel>
{
public TestModelValidator()
{
RuleFor(o => o.ParentVal).GreaterThan(0);
RuleFor(o => o.Sub.SubDataVal).GreaterThan(0);
}
}
And when I write the following unit test
[Test]
public void Should_have_error_when_val_is_zero()
{
validator = new TestModelValidator();
validator.ShouldHaveValidationErrorFor(model => model.ParentVal, 0);
}
I get a "System.NullReferenceException : Object reference not set to an instance of an object." exception from FluentValidation.TestHelper.ValidatorTester`2.ValidateError(T instanceToValidate)
(if I remove the RuleFor(o => o.Sub.SubDataVal).GreaterThan(0); line, then it works!)
Similarly if I try and unit test the actual child class with:
[Test]
public void Should_have_error_when_sub_dataVal_is_zero()
{
validator = new TestModelValidator();
validator.ShouldHaveValidationErrorFor(model => model.Sub.SubDataVal, 0);
}
I get a "System.Reflection.TargetException : Object does not match target type." from FluentValidation.TestHelper.ValidatorTester`2.ValidateError(T instanceToValidate)
Upvotes: 13
Views: 24731
Reputation: 1
For anyone getting here as I did.
As all rules are executed during the Validation you need to set the whole model to reasonable values.
With current version you can do following:
//using FluentValidation;
//using FluentValidation.TestHelper;
[Fact]
public void Should_have_error_when_val_is_zero()
{
var validator = new TestModelValidator();
var model = new TestModel()
{
ParentVal = 0,
Sub = new TestModel.SubData()
};
var result = validator.TestValidate(model);
result.ShouldHaveValidationErrorFor(model => model.ParentVal);
//result.ShouldHaveValidationErrorFor(nameof(TestModel.ParentVal));
}
Upvotes: 0
Reputation: 1236
With MSTest and FluentAssertions you can write
[TestMethod]
public void Should_have_error_when_val_is_zero()
{
// Given
var validator = new TestModelValidator();
var testModel = TestModel
{
ParentVal = 0
}; // You should create a invalid TestModel object here
// When
validator.Validate(testModel).IsValid.Should().BeFalse();
}
Upvotes: 2
Reputation: 1869
You can unit test models and child models but you will need to change your validation class to use a separate validator class which just validates the child model:
public class TestModelValidator : AbstractValidator<TestModel>
{
public TestModelValidator()
{
RuleFor(o => o.ParentVal).GreaterThan(0);
RuleFor(o => o.Sub).SetValidator(new SubDataValidator());
}
}
public class SubDataValidator : AbstractValidator<SubData>
{
public SubDataValidator()
{
RuleFor(o => o.SubDataVal).GreaterThan(0);
}
}
You can then write your unit tests to test each validator or both together.
Upvotes: 10
Reputation: 1170
I have come to the conclusion that for this ShouldHaveValidationErrorFor is just not capabable of dealing with subclasses, so have resorted to doing it manually. i.e.
[Test]
public void Should_have_error_when_val_is_zero()
{
validator = new TestModelValidator();
TestModel testRequest = new TestModel();
//populate with dummy data
var result = validator.Validate(testRequest);
Assert.That(result.Errors.Any(o => o.PropertyName== "ParentVal"));
}
Upvotes: 9