Skip to content

Changing the behavior of WhenTypeIs for value types #799

@jnyrup

Description

@jnyrup

Currently WhenTypeIs checks the runtime type of the expectation.
This can cause a NullReferenceException in CreateFromEquivalencyValidationContext in the following example.

var obj1 = new { date = (DateTime?)null };
var obj2 = new { date = (DateTime?)default(DateTime) };
var actual = new[] { obj1, obj2 };
var expected = new[] { obj2, obj1 };

actual.Should().BeEquivalentTo(expected, options => options
	.Using<DateTime>(ctx => ctx.Subject.Should().BeCloseTo(ctx.Expectation, 1.Seconds()))
	.WhenTypeIs<DateTime>());

As the runtime type of default(DateTime) is DateTime, WhenTypeIs will match.
CreateFromEquivalencyValidationContext will then cast the null subject to a DateTime causing a NullReferenceException.

Another case is when a struct implements an interface, and CreateFromEquivalencyValidationContext cast a null into the struct type.

I propose that we change the logic of WhenTypeIs to consider whether the RuntimeType or CompileTimeType is a value type.

I have implemented the proposal here.
It is not a compile-breaking change, but if you relied on WhenTypeIs<DateTime> to match DateTime? it will break your code.
As there aren't that many tests of WhenTypeIs I wanted to start a discussion on the intended/expected behavior before submitting a PR:

This was inspired by the problem presented in #778 .

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions