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:
- You have no reference to the string you pass in as a parameter, so you have to have it in there as a string literal twice. You can work around this with string constants within the test class, for example.
- The number of types that can be used as attribute parameters in .NET is limited. As long as you only need basic types, you should be fine, but calling constructors or the like in the parameter list is not possible.
- You should only use this attribute when you need an instance if
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.
- If you need the capabilities of the
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?