Given this system to test:
public class MySut
{
private readonly IHardToMockDependency _hardToMockDependency;
public MySut(IHardToMockDependency hardToMockDependency,
IOtherDependency otherDependency)
{
_hardToMockDependency = hardToMockDependency;
}
public string GetResult()
{
return _hardToMockDependency.GetResult();
}
}
public interface IOtherDependency { }
public interface IHardToMockDependency
{
string GetResult();
}
And this unit test:
internal class FakeHardToMockDependency : IHardToMockDependency
{
private readonly string _result;
public FakeHardToMockDependency(string result)
{
_result = result;
}
public string GetResult()
{
return _result;
}
}
public class MyTests
{
[Fact]
public void GetResultReturnsExpected()
{
string expectedResult = "what I want";
var otherDependencyDummy = new Mock<IOtherDependency>();
var sut = new MySut(new FakeHardToMockDependency(expectedResult),
otherDependencyDummy.Object);
var actualResult = sut.GetResult();
Assert.Equal(expectedResult, actualResult);
}
}
How should I convert it to use AutoFixture.Xunit and AutoFixture.AutoMoq (while still using the manual fake)?
In real-world tests, the manual fake would have a more complex interface and behavior. Note that I want to pass the anonymous variable (the expectedResult string) to the constructor of the manual fake.
There are already some good answers here, but I'd like to suggest a simpler alternative that involves slightly loosening the invariants of the FakeHardToMockDependency class. Make it public, and provide a way to assign the result that is decoupled from the constructor:
public class FakeHardToMockDependency : IHardToMockDependency
{
private string _result;
public FakeHardToMockDependency(string result)
{
_result = result;
}
internal string Result
{
get { return _result; }
set { _result = value; }
}
public string GetResult()
{
return _result;
}
}
Notice that I've added an internal property and removed the readonly
keyword from the field.
This enables you to refactor the original test to this:
[Theory, AutoMoqData]
public void GetResultReturnsExpected_AutoDataVersion(
[Frozen(As = typeof(IHardToMockDependency))]FakeHardToMockDependency fake,
MySut sut)
{
var expected = "what I want";
fake.Result = expected;
var actual = sut.GetResult();
Assert.Equal(expected, actual);
}
For completeness, here's the AutoMoqDataAttribute code:
public class AutoMoqDataAttribute : AutoDataAttribute
{
public AutoMoqDataAttribute()
: base(new Fixture().Customize(new AutoMoqCustomization()))
{
}
}
Depending on what kind of parameters you need to pass to your manual fake, you might be able to use a parameterized attribute, similar to the AutoFixture's built-in InlineAutoDataAttribute
.
Given these
public interface IHardToMockDependency
{
string Value { get; }
}
public class FakeHardToMockDependency : IHardToMockDependency
{
private readonly string _value;
public FakeHardToMockDependency(string value)
{
_value = value;
}
#region IHardToMockDependency Members
public string Value
{
get { return this._value; }
}
#endregion IHardToMockDependency Members
}
you create an ICustomization
implementation that tells a fixture object how to create an implemention of the IHardToFakeDependency
interface:
public class FakeHardToMockDependencyCustomization : ICustomization
{
private readonly string _value;
public FakeHardToMockDependencyCustomization(string value)
{
_value = value;
}
#region ICustomization Members
public void Customize(IFixture fixture)
{
fixture.Register<IHardToMockDependency>(() => new FakeHardToMockDependency(this._value));
}
#endregion ICustomization Members
}
Note that this needs to know the string you want to pass in, of course.
Next, you roll up this with the other customizations you want to use in a CompositeCustomization
:
public class ManualFakeTestConventions : CompositeCustomization
{
public ManualFakeTestConventions(string value)
: base(new FakeHardToMockDependencyCustomization(value), new AutoMoqCustomization())
{
}
}
Make sure you always put the customizations in order from most specific to most general, as explained here by Mark Seemann.
Now you create an AutoDataAttribute
implementation that uses this customization:
public class ManualFakeAutoDataAttribute : AutoDataAttribute
{
public ManualFakeAutoDataAttribute(string value)
: base(new Fixture().Customize(new ManualFakeTestConventions(value)))
{
}
}
This can now be used in the same way as an InlineAutoDataAttribute
:
public class ManualFakeTests
{
[Theory, ManualFakeAutoData("iksdee")]
public void ManualFake(IHardToMockDependency fake)
{
Assert.IsType<FakeHardToMockDependency>(fake);
Assert.Equal("iksdee", fake.Value);
}
}
You can also have it injected into an auto-created SUT instance right away by applying the [Frozen]
attribute to the Theory parameter:
[Theory, ManualFakeAutoData("iksdee")]
public void SutWithManualFake([Frozen] IHardToMockDependency fake, MySut sut)
{
}
This will create a MySut
instance and the IHardToMockDependency
instance needed for the constructor, for which you have given AutoFixture a rule in the FakeHardToMockDependencyCustomization
, and also give you that very instance as the fake
variable.
Note that not freezing the fake would still give you a correct FakeHardToMockDependency
instance as well as inject one into the sut, but those would be distinct, as we have registered a factory delegate in the customization. Freezing the instance will cause the fixture to always return the same instance for subsequent requests for the interface.
This has a few caveats, however:
IHardToFakeDependency
; otherwise you'll always have to pass in a string parameter anyway. If you have a set of standard customizations you need to use, create another attribute that contains only those.InlineAutoDataAttribute
at the same time, you also need to create yet another attribute that combines the features of both.Depending on the concrete circumstances, you might also want to look at xUnit.net's PropertyDataAttribute
, but I hardly ever find myself using that.
In general, in my opinion, understanding how to work with customizations and autodata attributes and when and how to create your own are the key to effectively using AutoFixture and really make it save you work.
If you often write code in a specific domain that you need to test, it might well make sense to create a library containing customizations, attribute and stub objects that will always be ready to use once you drop it in next to xUnit.net, AutoFixture and Moq. I know I'm damn glad I built mine.
Oh, and also: Having a dependency that is hard to mock might point at a design issue. Why is it that hard to mock?
Maybe this is not the most idiomatic Autofixture setup but definitely works:
[Fact]
public void GetResultReturnsExpected()
{
var fixture = new Fixture()
.Customize(new AutoMoqCustomization());
var expectedResult = fixture.Create<string>();
fixture.Register<IHardToMockDependency>(
() => new FakeHardToMockDependency(expectedResult));
var sut = fixture.Create<MySut>();
var actualResult = sut.GetResult();
Assert.Equal(expectedResult, actualResult);
}
If you want to use also AutoData
you can create your own AutoMoqData
based on this great article where you can hide some or all of the fixture custimizations.
Something like:
public class MySutAutoDataAttribute : AutoDataAttribute
{
public MySutAutoData()
: base(new Fixture()
.Customize(new AutoMoqCustomization()))
{
Fixture.Freeze<string>();
Fixture.Register<IHardToMockDependency>(
() => new FakeHardToMockDependency(Fixture.Create<string>()));
}
}
And you can use it like:
[Theory, MySutAutoData]
public void GetResultReturnsExpected(MySut sut, string expectedResult)
{
var actualResult = sut.GetResult();
Assert.Equal(expectedResult, actualResult);
}
But you should note that there is room for a lots of improvement in the MySutAutoDataAttribute
for example: it is not very generic and Fixture.Freeze<string>();
can cause problems if you are using multiple strings in your tests.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With