Fluent Assertions - Assert properties with same names but different types

159 views Asked by At

I get this (expected) error when asserting properties with different types:

Expected property a.Date to be System.String, but found System.DateTime.

I would like to assert the equality between a string representation of a date to a nullable DateTime without doing a manual assert as I would rather try to specify some kind of a rule or relationship due to a high number of properties to be asserted, than first excluding them from the BeEquivalentTo and then assert them one-by-one with: a.DateX.Should().Be(b.DateX.ToNullableDateTime());

I have a minimal example below just to demonstrate the issue:

[TestFixture]
public class MyTests 
{
   [Test]
    public void ShouldMapDates()
    {
        //Arrange
        var b = new B { Date = "01.01.0001" };

        //Act
        var a = B.MapToA(b);

        //Assert
        a.Should().BeEquivalentTo(b, c =>
            {
                //Magic configuration here?
            });
    }
}

public class A
{
    public DateTime? Date { get; set; }
}

public class B
{
    public string Date { get; set; }

    public static A MapToA(B b)
    {
        return new A { Date = b.Date.ToNullableDateTime() };
    }
}

Any ideas?

ToNullableDateTime is an extension method and I expect that I would have to use it in the assertion config somehow. I also have an extension method on DateTime? to convert back to the same string format at my disposal

Using FluentAssertions v6.11

Edit Solved:

public class DateEqualityStep : IEquivalencyStep
{
    public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationContext context, IEquivalencyValidator nestedValidator)
    {
        if (context.CurrentNode.IsRoot)
        {
            return EquivalencyResult.ContinueWithNext;
        }

        if (comparands.Subject?.GetType() != typeof(DateTime) || comparands.RuntimeType != typeof(string))
        {
            return EquivalencyResult.ContinueWithNext;
        }

        var subject = (DateTime?)comparands.Subject;
        var expectation = (string)comparands.Expectation;
        subject.Should().Be(expectation.ToNullableDateTime(), context.Reason.FormattedMessage, context.Reason.Arguments);
        return EquivalencyResult.AssertionCompleted;
    }
}

Usage:

a.Should().BeEquivalentTo(b, c => c.Using(new DateEqualityStep()));

This is good enough for my scenario

1

There are 1 answers

1
Fildor On

I'd do this:

[TestFixture]
public class MyTests() 
{
   [Test]
    public void ShouldMapDates()
    {
        //Arrange
        var stringDate = "01.01.0001";
        var b = new B { Date = stringDate };
        var expected = new A { Date = DateTime.Parse(stringDate); } // You _may_ need to tweek parsing, so that it actually works. Maybe use ParseExact?

        //Act
        var a = B.MapToA(b);

        //Assert
        a.Should().BeEquivalentTo(expected);
    }
}

public class A
{
    public DateTime? Date { get; set; }
}

public class B
{
    public string Date { get; set; }

    public static A MapToA(B b)
    {
        return new A { Date = b.Date.ToNullableDateTime() };
    }
}

This avoids your problem entirely.


Addendum: I'd also write a set of unit tests for ToNullableDateTime().