Implementing an Interface and Implementing in AutoFixture

Consider the following classes:

public interface IInterface {} public class Class : IInterface {} public class Customization : ICustomization { readonly IInterface item; public Customization() : this( new Class() ) {} public Customization( IInterface item ) { this.item = item; } public void Customize( IFixture fixture ) { fixture.Inject( item ); var created = fixture.Create<Class>(); // Would like this to resolve as item from previous line. } } 

The problem I am facing is that IInterface is being introduced, but Class is not. Is there a way to insert both IInterface and Class so that the same instance is returned for both?

Note that I would like to do this using ICustomization (or inside ICustomization ), and not with test method attributes. I am looking to make individual injections in these two classes. If I use the [Frozen( Matching.ImplementedInterfaces)]Class item as a parameter, it does not work, since the class, which is Frozen, overwrites the entered value in the ICustomization.Customize method.

Also, note that this is sample code, not how I use it. In the xUnit Test method, I would like the Class instance to be specified as a parameter for the frozen IInstance above:

 public void MyTest( IInterface @interface, Class implementation ) { Assert.Same( @interface, implementation ); } 
+5
source share
3 answers

OK, it took forever to understand, but this question / scenario is ultimately due to poor design on my part and the inexperienced experience and training of AutoFixture. The actual scenario of what I was trying to do was register the IoC container with my device, and I wanted IServiceLocator and its implementation to be registered in Fixture so that they were available for entering values ​​into the current test method.

This was resolved by adding a setting to pass requests to the provided IServiceLocator (also mentioned here in this question ).

In addition, I made an extension method that uses Dynamitey , which does what I was looking for, but it is no longer in use, and I will probably delete it at some point.

So the answer is: if you want to do this for some reason, you are more than likely doing it wrong . I'm halfway tempted to delete this answer, but I will leave it here if another newb like me is facing the same problem and can benefit from it.

Finally, I would like to thank @ enrico-campidoglio and @ mark-seemann for their patience and really informative / high-quality answers / help (and also for the fact that no one voted for this question). I know the bar is set high here by @Stack Overflow, and I probably should have had a little more patience before posting this question.

0
source

If you absolutely must do it

If you look closer at the Inject method, you will notice that this is actually a general method, but the type argument is output when you use it, how you use it. If you want to freeze both, you can - you just need to call Inject for each type.

Customization slightly modified here. To prevent a possible invalid downgrade, I changed it so that its item field (and the corresponding argument to the item constructor) is of type Class :

 public class Customization : ICustomization { readonly Class item; public Customization() : this(new Class()) { } public Customization(Class item) { this.item = item; } public void Customize(IFixture fixture) { fixture.Inject(item); fixture.Inject<IInterface>(item); } } 

Note that Customize introduces the same item twice. In the first line, the generic type argument is output by the Class compiler, while in the second line, the IInterface type IInterface explicitly defined.

Given this implementation, the following test passes:

 [Fact] public void UseCustomization() { var fixture = new Fixture().Customize(new Customization()); var c = fixture.Create<Class>(); var i = fixture.Create<IInterface>(); Assert.Equal(c, i); } 

Using the built-in API

All that said, I would find it easier to just use the built-in API:

 [Fact] public void UseTypeRelay() { var fixture = new Fixture(); fixture.Customizations.Add( new TypeRelay( typeof(IInterface), typeof(Class))); fixture.Freeze<Class>(); var c = fixture.Create<Class>(); var i = fixture.Create<IInterface>(); Assert.Equal(c, i); } 

TypeRelay maps IInterface to Class , which means that all requests for IInterface will be passed to requests for Class . When Class frozen, it means that not only Class frozen, but there is IInterface .

+4
source

Of course, you can apply the [Frozen] attribute to a specific class parameter and specify ImplementedInterfaces as matching criteria:

 [Theory, AutoData] public void Test( [Frozen(Matching.ImplementedInterfaces)]Class implementation, IInterface @interface) { Assert.Same(implementation, @interface); } 

This means that AutoFixture provides the same instance of Class every time it needs to create a value for any of its implemented interfaces.

+2
source

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


All Articles