0

I'd like to tell FluentAssertions to check if 2 instances are of the same type, but for a nested property.

The scenario is the following:

This is the class

class Item
{
    public int? Value { get; set; }
    public Exception? Exception { get; set; }
}

Then, I have a test:

[Fact]
public void Test()
{
    var expected = new[] 
    { 
        new Item { Value = 1 },
        new Item { Exception = new TimeoutException() },
    };

    var actual= new[] 
    { 
        new Item { Value = 1 },
        new Item { Exception = new TimeoutException() },
    };

    actual.Should().BeEquivalentTo(expected);
}

The test fails because the two instances of TimeoutException are different. I don't know to test for instance being equal, but being of the same type.

How can I do it?

NOTE I actually have managed to get the correct behavior doing this:

actual.Should().BeEquivalentTo(expected, options => options
    .Using<Item>(context =>
    {
        if (context.Expectation.Exception is { Exception: { } a } && context.Subject.Exception is { Exception: { } b })
        {
            a.Should().BeOfType(b.GetType());
        }
    })
    .WhenTypeIs<Item>());

But it feels wrong and suboptimal.

Jonas Nyrup
  • 2,376
  • 18
  • 25
SuperJMN
  • 13,110
  • 16
  • 86
  • 185

1 Answers1

0

You're almost there with your attempt, but there are two cases it cannot handle.

  • It now ignores Item.Value
  • If exactly one of the Exception properties are null, it does not fail
public class UnitTest1
{
    [Theory]
    [MemberData(nameof(GoodData))]
    public void Success(Item[] actual, Item[] expected)
    {
        actual.Should().BeEquivalentTo(expected, options => options
            .Using<Exception>(ctx =>
            {
                var subjectType = ctx.Subject?.GetType();
                var expectedType = ctx.Expectation?.GetType();

                subjectType.Should().BeSameAs(expectedType);
            })
            .When(e => e.ParentType == typeof(Item) && e.Path.EndsWith(".Exception")));
    }

    [Theory]
    [MemberData(nameof(BadData))]
    public void Fail(Item[] actual, Item[] expected)
    {
        actual.Should().NotBeEquivalentTo(expected, options => options
            .Using<Exception>(ctx =>
            {
                var subjectType = ctx.Subject?.GetType();
                var expectedType = ctx.Expectation?.GetType();

                subjectType.Should().BeSameAs(expectedType);
            })
            .When(e => e.ParentType == typeof(Item) && e.Path.EndsWith(".Exception")));
    }

    public static TheoryData<Item[], Item[]> BadData => new()
    {
        {
            new Item[] { new() { Value = 1 } },
            new Item[] { new() { Value = 2 } }
        },
        {
            new Item[] { new() { Exception = new TimeoutException() } },
            new Item[] { new() { Exception = null } }
        },
        {
            new Item[] { new() { Exception = null } },
            new Item[] { new() { Exception = new TimeoutException() } }
        },
        {
            new Item[] { new() { Exception = new ArgumentException() } },
            new Item[] { new() { Exception = new TimeoutException() } }
        }
    };

    public static TheoryData<Item[], Item[]> GoodData => new()
    {
        {
            new Item[] { new() },
            new Item[] { new() }
        },
        {
            new Item[] { new() { Value = 1 } },
            new Item[] { new() { Value = 1 } }
        },
        {
            new Item[] { new() { Exception = new TimeoutException() } },
            new Item[] { new() { Exception = new TimeoutException() } }
        }
    };
}

public class Item
{
    public int? Value { get; set; }
    public Exception? Exception { get; set; }
}
Jonas Nyrup
  • 2,376
  • 18
  • 25