Reputation: 15432
I was writing a unit test for ApiControllerConventions
from a related SO question and I wrote a AutoMoqApiControllerDataAttribute
to integrate this ICustomization
in xUnit.net.
public class AutoMoqApiControllerDataAttribute : AutoDataAttribute
{
public AutoMoqApiControllerDataAttribute() : this(new Fixture())
{
}
public AutoMoqApiControllerDataAttribute(IFixture fixture) : base(
fixture.Customize(new ApiControllerConventions()))
{
}
}
I tried writing a unit test for this attribute like this:
public class AutoMoqDataAttribute : AutoDataAttribute
{
public AutoMoqDataAttribute() : this(new Fixture())
{
}
public AutoMoqDataAttribute(IFixture fixture) :
base(fixture.Customize(new AutoMoqCustomization()))
{
}
}
[Theory, AutoMoqData]
public void AutoMoqApiControllerDataAttribute_ContainsCorrectCustomization(
Mock<IFixture> mockFixture)
{
// Arrange
mockFixture.Setup(f => f.Customize(It.IsAny<ApiControllerConventions>())).Verifiable();
// Act
var sut = new AutoMoqApiControllerDataAttribute(mockFixture.Object);
// Assert
mockFixture.Verify(f => f.Customize(It.IsAny<ApiControllerConventions>()), Times.Once);
}
This fails with an exception from AutoDataAttribute
:
System.ArgumentNullException Value cannot be null. Parameter name: fixture at Ploeh.AutoFixture.Xunit2.AutoDataAttribute..ctor(IFixture fixture)
I thought this is related to my usage of the AutoMoqDataAttribute
so I reverted to a vanilla Fact
, same exception still:
[Fact]
public void AutoMoqApiControllerDataAttribute_ContainsCorrectCustomization()
{
// Arrange
var mockFixture = new Mock<IFixture>();
mockFixture.Setup(f => f.Customize(It.IsAny<ApiControllerConventions>())).Verifiable();
// Act
var sut = new AutoMoqApiControllerDataAttribute(mockFixture.Object);
// Assert
mockFixture.Verify(f => f.Customize(It.IsAny<ApiControllerConventions>()), Times.Once);
}
Why am I getting the exception? I obviously have the IFixture
mocked and it is definitely not null.
Upvotes: 1
Views: 1972
Reputation: 233125
When you get an exception, it's always a good idea to look at the call stack. The exception isn't thrown in AutoMoqApiControllerDataAttribute
, but in AutoDataAttribute
:
System.ArgumentNullException : Value cannot be null.
Parameter name: fixture
at Ploeh.AutoFixture.Xunit2.AutoDataAttribute..ctor(IFixture fixture)
AutoMoqApiControllerDataAttribute.cs(12,0): at _44380395.AutoMoqApiControllerDataAttribute..ctor(IFixture fixture)
Tests.cs(22,0): at _44380395.Tests.AutoMoqApiControllerDataAttribute_ContainsCorrectCustomization()
While fixture
isn't null
when it's passed to AutoMoqApiControllerDataAttribute
, it is null
when passed to base(IFixture)
.
Why?
Consider the call to base
:
base(fixture.Customize(new ApiControllerConventions()))
Which value is being passed to base
?
Not fixture
, but the return value from invoking fixture.Customize
.
The test doesn't set up a return value for this method call, so Moq defaults to the default value for the type. Since IFixture
is a reference type, that means null
, and that null
value is then passed to base
.
You can easily address the problem by configuring mockFixture
with a return value in the test:
[Fact]
public void AutoMoqApiControllerDataAttribute_ContainsCorrectCustomization()
{
// Arrange
var mockFixture = new Mock<IFixture>();
mockFixture
.Setup(f => f.Customize(It.IsAny<ApiControllerConventions>()))
.Returns(mockFixture.Object)
.Verifiable();
// Act
var sut = new AutoMoqApiControllerDataAttribute(mockFixture.Object);
// Assert
mockFixture.Verify(
f => f.Customize(It.IsAny<ApiControllerConventions>()),
Times.Once());
}
Now the test passes in my repro.
This isn't a problem specific to AutoFixture. You could reproduce it with any Fluent Interface.
Upvotes: 2