Skip to content

Commit

Permalink
Fix NullReferenceException when passing null to a nullable argument b…
Browse files Browse the repository at this point in the history
…ut trying to match it with a non-nullable IsAny.

This fixes issue devlooped#90.

The problem was that Matches() has to cast its input to the type parameter T in order to call the Condition function.
Since T is a reference type, it does not accept nulls and throws a NullReferenceException at the site of the cast.

We get around this by treating value types as a special case. If T is a value type and the input is null, it clearly doesn't match (since value types are not nullable).
  • Loading branch information
Benjamin Hodgson committed Mar 10, 2015
1 parent 92e8ed9 commit cc45976
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 2 deletions.
15 changes: 13 additions & 2 deletions Source/Match.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,19 @@ internal override bool Matches(object value)
{
return false;
}

if (value == null && typeof(T).IsValueType)
{
// If this.Condition expects a value type and we've been passed null,
// it can't possibly match.
// This tends to happen when you are trying to match a parameter of type int?
// with IsAny<int> but then pass null into the mock.
// We have to return early from here because you can't cast null to T
// when T is a value type.
//
// See Github issue #90: https://github.com/Moq/moq4/issues/90
return false;
}
return this.Condition((T)value);
}
}
}
}
12 changes: 12 additions & 0 deletions UnitTests/MatchersFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,17 @@ public void DoesNotMatchDifferentEnumerableParameterValue()
Assert.Equal(0, mock.Object.DoAddition(new[] { 2, 4, 6, 8 }));
}

[Fact]
public void MatchingNonNullableValueTypeForNullableParameterDoesNotMatchNull()
{
var mock = new Mock<IFoo>();

mock.Setup(x => x.TakesNullableParameter(It.IsAny<int>())).Returns(5);

Assert.Equal(5, mock.Object.TakesNullableParameter(5));
Assert.Equal(0, mock.Object.TakesNullableParameter(null));
}

private int GetToRange()
{
return 5;
Expand All @@ -271,6 +282,7 @@ public interface IFoo
bool DoTypeOverload(Baz baz);
int DoAddition(int[] numbers);
int[] Items { get; set; }
int TakesNullableParameter(int? value);
}
}
}

0 comments on commit cc45976

Please sign in to comment.