Skip to content

Commit

Permalink
Add more descriptive error messages if a rule throws a NullReferenceE…
Browse files Browse the repository at this point in the history
…xception (#2153)

Co-authored-by: Jeremy Skinner <90130+JeremySkinner@users.noreply.github.com>
  • Loading branch information
MeikelLP and JeremySkinner committed Nov 24, 2023
1 parent 04f1a42 commit b3b95c4
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 5 deletions.
5 changes: 3 additions & 2 deletions Changelog.txt
@@ -1,5 +1,6 @@
11.8.2 -
11.9.0 -
Fix memory leak in NotEmptyValidator/EmptyValidator (#2174)
Add more descriptive error messages if a rule throws a NullReferenceException (#2152)

11.8.1 - 22 Nov 2023
Fix unintentional behavioural changes in introduced in the previous release as part of #2158
Expand Down Expand Up @@ -34,7 +35,7 @@ Deprecated the ability to disable the root model null check via overriding Abstr
Deprecated the Transform and TransformAsync methods (#2072)

11.5.0 - 13 Feb 2023
MemberNameValidatorSelector now supports wildcard indexes in property paths (#2056)
MemberNameValidatorSelector now supports wildcard indexes in property paths (#2056)
Added overload of TestValidateAsync that accepts a context (#2052)
Minor optimization to regex validator (#2035)
Added Kazakh translations (#2036)
Expand Down
31 changes: 30 additions & 1 deletion src/FluentValidation.Tests/NullTester.cs
Expand Up @@ -18,6 +18,8 @@

namespace FluentValidation.Tests;

using System;
using System.Collections.Generic;
using System.Linq;
using Xunit;

Expand Down Expand Up @@ -60,4 +62,31 @@ public class NullTester {
var result = validator.Validate(new Person());
result.IsValid.ShouldBeTrue();
}
}

[Fact]
public void NullProperty_should_throw_NullReferenceException() {
var validator = new InlineValidator<Person>();
validator.RuleFor(x => x.Orders.Count).NotEmpty();

var ex = Assert.Throws<NullReferenceException>(() => validator.Validate(new Person {
Orders = null
}));

ex.Message.ShouldEqual("NullReferenceException occurred when executing rule for x => x.Orders.Count. If this property can be null you should add a null check using a When condition");
ex.InnerException.ShouldNotBeNull();
ex.InnerException!.GetType().ShouldEqual(typeof(NullReferenceException));
}

[Fact]
public void ForEachNullProperty_should_throw_NullReferenceException_when_exception_occurs() {
var validator = new InlineValidator<Person>();
validator.RuleForEach(x => x.Orders[0].Payments).NotNull();

var ex = Assert.Throws<NullReferenceException>(() => validator.Validate(new Person {
Orders = null
}));
ex.Message.ShouldEqual("NullReferenceException occurred when executing rule for x => x.Orders.get_Item(0).Payments. If this property can be null you should add a null check using a When condition");
ex.InnerException.ShouldNotBeNull();
ex.InnerException!.GetType().ShouldEqual(typeof(NullReferenceException));
}
}
2 changes: 2 additions & 0 deletions src/FluentValidation.Tests/Person.cs
Expand Up @@ -46,6 +46,8 @@ public class Person {
public Address Address { get; set; }
public IList<Order> Orders { get; set; }

public Order LatestOrder { get; set; }

public string Email { get; set; }
public decimal Discount { get; set; }

Expand Down
9 changes: 8 additions & 1 deletion src/FluentValidation/Internal/CollectionPropertyRule.cs
Expand Up @@ -134,7 +134,14 @@ public CollectionPropertyRule(MemberInfo member, Func<T, IEnumerable<TElement>>
}

var cascade = CascadeMode;
var collection = PropertyFunc(context.InstanceToValidate) as IEnumerable<TElement>;
IEnumerable<TElement> collection;

try {
collection = PropertyFunc(context.InstanceToValidate);
}
catch (NullReferenceException nre) {
throw new NullReferenceException($"NullReferenceException occurred when executing rule for {Expression}. If this property can be null you should add a null check using a When condition", nre);
}

int count = 0;
int totalFailures = context.Failures.Count;
Expand Down
7 changes: 6 additions & 1 deletion src/FluentValidation/Internal/PropertyRule.cs
Expand Up @@ -145,7 +145,12 @@ TProperty PropertyFunc(T instance)

if (first) {
first = false;
propValue = PropertyFunc(context.InstanceToValidate);
try {
propValue = PropertyFunc(context.InstanceToValidate);
}
catch (NullReferenceException nre) {
throw new NullReferenceException($"NullReferenceException occurred when executing rule for {Expression}. If this property can be null you should add a null check using a When condition", nre);
}
}

bool valid = await component.ValidateAsync(context, propValue, useAsync, cancellation);
Expand Down

0 comments on commit b3b95c4

Please sign in to comment.