I have previously asked a similar question on SO to which I got an answer. At the time, for the sake of expediency, I mechanically applied the answer but now I'm trying to get a handle on how the mechanism for declaratively setting up a fixture is.
So, I am currently looking at Mark Seemann's Dealing With Types Without Public Constructors blog post and converting it to be declarative. It is very similar to my original query but I can't get it to work. Please note that the code given is not actually production code and that this is a learning exercise.
Now if it helps, I've got the imperative code up on GitHub and the code in question is reproduced below:
[Fact]
public static void CanOverrideCtorArgs()
{
var fixture = new Fixture();
var knownText = "This text is not anonymous";
fixture.Register<int, IMyInterface>( i => new FakeMyInterface( i, knownText ) );
var sut = fixture.Create<MyClass>();
}
This is the code similar to the one given in this post.
Hence, my question is what should I know/read in order to convert this snippet of imperative code to be declarative.
Go read
for great examples of customizations and how to package, mix and mingle them.
Main principle is that you make your Customizations as granular as possible.
Then, you need to feed them in to the processing pipeline via either:
AutoData
-derived attributes for global stuff (i.e. like MyTestConventions
in Mark's answer )CustomizeWith
helper[1] or similar[Freeze( As ... )]
Automating this, I'd write:
[Theory, AutoData]
public static void OutOfBandCustomization(
[CustomizeWith( typeof( MyFakerCustomization ) )] MyClass sut )
{
}
Using this customization:
public class MyFakerCustomization : ICustomization
{
void ICustomization.Customize( IFixture fixture )
{
var knownText = "This text is not anonymous";
fixture.Register<int, IMyInterface>( i =>
new FakeMyInterface( i, knownText ) );
}
}
Obviously registering a string
and/or using AutoMoqCustomization
might also be useful.
[1] CustomizeWith
is this helper Attribute (hat tip to Adam Jasinski):
[AttributeUsage( AttributeTargets.Parameter, AllowMultiple = true )]
sealed class CustomizeWithAttribute : CustomizeAttribute
{
readonly Type _type;
public CustomizeWithAttribute( Type customizationType )
{
if ( customizationType == null )
throw new ArgumentNullException( "customizationType" );
if ( !typeof( ICustomization ).IsAssignableFrom( customizationType ) )
throw new ArgumentException(
"Type needs to implement ICustomization" );
_type = customizationType;
}
public override ICustomization GetCustomization( ParameterInfo parameter )
{
return (ICustomization)Activator.CreateInstance( _type );
}
}
One tip: you can express
fixture.Register<int, IMyInterface>( i =>
new FakeMyInterface( i, knownText ) );
as
fixture.Customize<IMyInterface>(c =>c.FromFactory((int i)=>
new FakeMyInterface(i,knownText)));
too. While this doesn't make your case easier, it is a more general way of customizing what's going on.
Internally, Register
is [currently]:
fixture.Customize<T>(c => c.FromFactory(creator).OmitAutoProperties());
First of all, I'm going to answer this question under the assumption that TypesWithoutPublicCtrs
is defined as in the OP's GitHub repository:
public class TypesWithoutPublicCtrs
{
private readonly IMyInterface _mi;
public TypesWithoutPublicCtrs(IMyInterface mi)
{
_mi = mi;
}
}
The reason I'm explicitly calling this out is because the name is a red herring: it does have a public constructor; it just doesn't have a default constructor.
Anyway, AutoFixture easily deals with the absence of default constructors. The problem here isn't the TypesWithoutPublicCtrs
class itself, but rather the IMyInterface
interface. Interfaces are problematic because they can't be initialized at all.
Thus, you need to somehow map an interface to a concrete class. There are various ways to do that.
One-off solution
Once in a while, I use this one-off solution, although I find it ugly. However, it's easy and doesn't require a lot of sophisticated setup.
[Theory, AutoData]
public void TestSomething(
[Frozen(As = typeof(IMyInterface))]FakeMyInterface dummy,
TypesWithoutPublicCtrs sut)
{
// use sut here, and ignore dummy
}
This isn't particularly nice, because it relies on a side-effect of the [Frozen]
attribute, but it works as a self-contained one-off solution.
Convention
However, I much rather like to make a convention out of it, so that the same convention applies for all tests in a test suite. A test using such a convention could look like this:
[Theory, MyTestConventions]
public void TestSomething(TypesWithoutPublicCtrs sut)
{
// use sut here; it'll automatically have had FakeMyInterface injected
}
The [MyTestConventions]
attribute could look like this:
public class MyTestConventionsAttribute : AutoDataAttribute
{
public MyTestConventionsAttribute() :
base(new Fixture().Customize(new MyTestConventions())
{}
}
The MyTestConventions
class must implement the interface ICustomization
. There are several ways in which you can map IMyInterface
to FakeMyInterface
; here's one:
public class MyTestConventions : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customizations.Add(
new TypeRelay(typeof(IMyInterface), typeof(FakeMyInterface)));
}
}
Auto-Mocking
However, you may get tired of having to create and maintain all those Fakes, so you can also turn AutoFixture into an Auto-Mocking Container. There are various options for doing that, leveraging Moq, NSubstitute, FakeItEasy and Rhino Mocks.
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