Reputation: 2129
When I use Fluent assertions to test if a property is not null, the analysers still complain about subsequent lines which dereference that line as possibly null. Is there any way to get the compiler to recognise the property as not being null after testing with FluentAssertions? e.g.
Foo? foo = Bar();
foo.Should().NotBeNull();
foo.Value.Should().Be(5); // Warning about dereference of a possibly null reference on this line
I know I can use a ! on the foo in the second line but is there any way to get the analysers to work this out for itself?
I did find this but I can't see how it can be used in this case.
Upvotes: 15
Views: 3821
Reputation: 45
Based on https://stackoverflow.com/a/69293843/2771689, I ended up doing it like this, since Should().NotBeNull()
throws anyway:
using FluentAssertions;
using FluentAssertions.Primitives;
using System.Diagnostics.CodeAnalysis;
public static class FluentAssertionsExtensions
{
/// <summary>
/// Extension to <see cref="FluentAssertions"/>'s
/// <see cref="ReferenceTypeAssertions{TSubject,TAssertions}.NotBeNull"/>, with proper nullability handling.
/// </summary>
/// <inheritdoc cref="ReferenceTypeAssertions{TSubject,TAssertions}.NotBeNull"/>
public static AndConstraint<ObjectAssertions> ShouldNotBeNull<T>(
[NotNull] this T? value,
string because = "",
params object[] becauseArgs)
{
#pragma warning disable CS8777
return value.Should().NotBeNull(because, becauseArgs);
#pragma warning restore CS8777
}
}
Upvotes: 3
Reputation: 66
You may use
(foo?.Value).Should().Be(5)
This will hit the alarm, when foo
is null
or when foo.Value
is not equal to 5
.
But beware of writing
foo?.Value.Should().Be(5)
because this won't throw any exception when foo
is null
!
Upvotes: 1
Reputation: 142963
There is not much you can do here (except raising the issue on github like one mentioned in this comment, or this one for nullable value types). Using MemberNotNullWhenAttribute
even if you had access to source code and compiled your version, I'm afraid, won't do much here, cause foo
is not a member of AndConstraint
returned by NotBeNull
.
So you have options of using !
here or chaining assertions which is a little bit cumbersome cause FluentAssertions
for some reason loses type information or writing your own method which wiil encapsulate this check.
For chaining assertions option you can do something like this:
foo.Should().NotBeNull()
.And
.Match<Foo>(f => f.Value == 5);
Or using BeEquivalentTo
for example:
foo.Should().NotBeNull()
.And
.BeEquivalentTo(new Foo {Value = 5});
Note that BeEquivalentTo
sometimes is pretty cumbersome to use also due to it's approach to comparing objects:
Objects are equivalent when both object graphs have equally named properties with the same value, irrespective of the type of those objects. Two properties are also equal if one type can be converted to another and the result is equal. The type of a collection property is ignored as long as the collection implements
IEnumerable<T>
and all items in the collection are structurally equal.
Upvotes: 0
Reputation: 311255
You could write your own alternative extension method:
public static void ShouldNotBeNull<T>([NotNull] this T? value)
{
// throw if null
}
Upvotes: 4