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

5

15

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.

Pansie answered 22/9, 2021 at 11:15 Comment(0)
D
4

You could write your own alternative extension method:

public static void ShouldNotBeNull<T>([NotNull] this T? value)
{
  // throw if null
}
Donadonadee answered 23/9, 2021 at 3:55 Comment(0)
M
3

Based on https://mcmap.net/q/785140/-using-fluentassertions-notbenull-isn-39-t-taken-into-account-by-nullable-analysers, 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
    }
}
Mullock answered 26/1, 2023 at 9:56 Comment(0)
E
1

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!

Ego answered 17/3, 2022 at 11:28 Comment(0)
E
1

You can chain with .And :

foo.Should().NotBeNull().And.Be(5);
Enloe answered 3/8, 2023 at 16:4 Comment(0)
A
0

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.

Anglia answered 22/9, 2021 at 14:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.