Search code examples
.netdatetimenodatimefluent-assertions

ShouldBeEquivalentTo failing for equivalent objects when the subject is a DateTime


What I'm trying to do

I've just set up a test to ensure that a NodaTime LocalDateTime is mapped to a .NET DateTime, retaining the same date and time values. I'm using FluentAssertions' ShouldBeEquivalentTo method to compare the corresponding property values.

[TestClass]
public class LocalDateTimeMapping
{
    [TestMethod]
    public void RetainsComponentValues()
    {
        var nodatimeTime = new LocalDateTime();
        var dotnetTime = nodatimeTime.ToDateTimeUnspecified();

        dotnetTime.ShouldBeEquivalentTo(nodatimeTime,
            o => o
                .Including(d => d.Year)
                .Including(d => d.Month)
                .Including(d => d.Day)
                .Including(d => d.Hour)
                .Including(d => d.Minute)
                .Including(d => d.Second)
                .Including(d => d.Millisecond)
        );
    }
}

The problem

The test is failing:

Expected subject to be 01/01/1970 00:00:00, but found <1970-01-01>.

With configuration:
- Select property System.DateTime.Year
- Select property System.DateTime.Month
- Select property System.DateTime.Day
- Select property System.DateTime.Hour
- Select property System.DateTime.Minute
- Select property System.DateTime.Second
- Select property System.DateTime.Millisecond
- Match property by name (or throw)
- Invoke Action<DateTime> when info.RuntimeType.IsSameOrInherits(System.DateTime)
- Invoke Action<String> when info.RuntimeType.IsSameOrInherits(System.String)

I don't know what the last two lines mean.

What I've tried

  1. Running the test in the debugger to confirm that the values of each of these were the same.
  2. Seeing if the problem was a specific property by removing the Includes for the different properties.
  3. Basing the configuration on EquivalencyAssertionOptions<DateTime>.Empty() to ensure that no extra checks or properties were implicitly involved.
  4. Simplifying it all down to just the following.

    dotnetTime.ShouldBeEquivalentTo(nodatimeTime,
        o => EquivalencyAssertionOptions<DateTime>.Empty()
    );
    

Solution

  • The following line from the error message indicated that some sort of special treatment was being given to the DateTime:

    Invoke Action<DateTime> when info.RuntimeType.IsSameOrInherits(System.DateTime)
    

    I tried swapping the two date-times I was comparing, and this resolved the problem:

    nodatimeTime.ShouldBeEquivalentTo(dotnetTime,
        o => o
            .Including(d => d.Year)
            .Including(d => d.Month)
            .Including(d => d.Day)
            .Including(d => d.Hour)
            .Including(d => d.Minute)
            .Including(d => d.Second)
            .Including(d => d.Millisecond)
    );
    

    So I conclude that ShouldBeEquivalentTo should not be called on a .NET DateTime.