What are the principles behind AutoFixture, a declarative way to create a fixture?

I previously asked a similar question , on which I received an answer. At that time, for the sake of expediency, I mechanically applied the answer, but now I'm trying to understand how the mechanism for declarative adjustment of reinforcement.

So, I am currently looking at Mark the Seemann Deployment With Types Without Public Constructors record and convert it to declarative. This is very similar to my original request, but I cannot get it to work. Please note that this code is not a production code and that this is an exercise.

Now, if that helps, I have imperative GitHub code , and the code below 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 code similar to the one given in this post .

Therefore, my question is what I need to know / read in order to convert this piece of imperative code into declarative.

+6
source share
2 answers

Go read

for great examples of settings and how to pack, mix and mix.

The basic principle is that you make your settings as possible as possible.

Then you need to send them to the processing pipeline via:

  • AutoData defined attributes for global material (i.e. as MyTestConventions in Mark's answer)
  • A CustomizeWith Helper [1] or similar
  • Cheating, for example, performing [Freeze( As ... )]

My implementation

By automating this, I would write:

 [Theory, AutoData] public static void OutOfBandCustomization( [CustomizeWith( typeof( MyFakerCustomization ) )] MyClass sut ) { } 

Using this setting:

 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, it may also be useful to register a string and / or use AutoMoqCustomization .

My common assistants

[1] CustomizeWith is a helper attribute ( Adam Jasinski hat tip):

 [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 ); } } 

Besides

One tip: you can express

 fixture.Register<int, IMyInterface>( i => new FakeMyInterface( i, knownText ) ); 

a

  fixture.Customize<IMyInterface>(c =>c.FromFactory((int i)=> new FakeMyInterface(i,knownText))); 

also. Although this does not make your case easier, it is a more general way of setting up what is happening.

Inside Register is [currently]:

 fixture.Customize<T>(c => c.FromFactory(creator).OmitAutoProperties()); 
+6
source

First of all, I'm going to answer this question under the assumption that TypesWithoutPublicCtrs defined as in the OP GitHub repository :

 public class TypesWithoutPublicCtrs { private readonly IMyInterface _mi; public TypesWithoutPublicCtrs(IMyInterface mi) { _mi = mi; } } 

The reason I explicitly call this is because the name is a red herring: it has an open constructor; it just does not have a default constructor.

In any case, AutoFixture easily copes with the lack of default constructors. The problem here is not in the TypesWithoutPublicCtrs class, but in the IMyInterface interface. Interfaces are problematic because they cannot be initialized at all.

Thus, you need to somehow map the interface to a specific class. There are various ways to do this.

One-time solution

From time to time I use this one-time solution, although I find it ugly. However, it is easy and does not require much complicated setup.

 [Theory, AutoData] public void TestSomething( [Frozen(As = typeof(IMyInterface))]FakeMyInterface dummy, TypesWithoutPublicCtrs sut) { // use sut here, and ignore dummy } 

This is not particularly pleasant because it relies on the side effect of the [Frozen] attribute, but it works as a stand-alone, one-time solution.

Convention

Nevertheless, I really want to make an agreement out of it, so the same agreement applies to all tests in the test suite. A test using such an agreement might look like this:

 [Theory, MyTestConventions] public void TestSomething(TypesWithoutPublicCtrs sut) { // use sut here; it'll automatically have had FakeMyInterface injected } 

The [MyTestConventions] attribute may look like this:

 public class MyTestConventionsAttribute : AutoDataAttribute { public MyTestConventionsAttribute() : base(new Fixture().Customize(new MyTestConventions()) {} } 

The MyTestConventions class must implement the ICustomization interface. There are several ways to map IMyInterface to FakeMyInterface ; here is one:

 public class MyTestConventions : ICustomization { public void Customize(IFixture fixture) { fixture.Customizations.Add( new TypeRelay(typeof(IMyInterface), typeof(FakeMyInterface))); } } 

Auto-Mocking

However, you may be tired of having to create and maintain all of these fakes, so you can also include AutoFixture in the Auto-Mocking Container . There are various options for this, using Moq , NSubstitute , FakeItEasy and Rhino Mocks .

+6
source

Source: https://habr.com/ru/post/945758/


All Articles