Mog0
Mog0

Reputation: 2129

Using FluentAssertions NotBeNull isn't taken into account by nullable analysers

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

Answers (5)

pushStack
pushStack

Reputation: 4333

You can chain with .And :

foo.Should().NotBeNull().And.Be(5);

Upvotes: 1

Reinier de Vries
Reinier de Vries

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

Harry von Borstel
Harry von Borstel

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

Guru Stron
Guru Stron

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

Drew Noakes
Drew Noakes

Reputation: 311255

You could write your own alternative extension method:

public static void ShouldNotBeNull<T>([NotNull] this T? value)
{
  // throw if null
}

Upvotes: 4

Related Questions