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 running into is that the IInterface
is injected, whereas the Class
is not. Is there a way to inject both IInterface
and Class
so that the same instance is returned for both?
Please note that I would like to do this using an ICustomization
(or within an ICustomization
) and not with the attributes on a test-method. I am looking to do customized inject on these two classes. If I use [Frozen( Matching.ImplementedInterfaces)]Class item
as a parameter, it doesn't work, as the Class that is Frozen overwrites the injected value in the ICustomization.Customize
method.
Please additionally note that this is sample code and not how I am using it. In the xUnit Test Method, I would like the Class
instance that is specified as a parameter to be the frozen IInstance
above:
public void MyTest( IInterface @interface, Class implementation )
{
Assert.Same( @interface, implementation );
}
Sure, you can apply the [Frozen]
attribute on the concrete class parameter and specify ImplementedInterfaces
as matching criteria:
[Theory, AutoData]
public void Test(
[Frozen(Matching.ImplementedInterfaces)]Class implementation,
IInterface @interface)
{
Assert.Same(implementation, @interface);
}
That tells AutoFixture to provide the same Class
instance every time it has to create a value for any of its implemented interfaces.
If you look closer at the Inject
method, you'll notice that it's actually a generic method, but that the type argument is inferred when you use it like you use it. If you want to freeze both, you can - you'll just have to invoke Inject
for each type.
Here's a slightly modified Customization
. In order to prevent a possibly invalid downcast, I changed it so that its item
field (and the corresponding item
constructor argument) is of the 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);
}
}
Notice that Customize
injects the same item twice. In the first line, the generic type argument is inferred to Class
by the compiler, whereas in the second line, the type argument IInterface
is 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);
}
All that said, I'd consider it easier to simply 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 relayed to requests for Class
. When Class
is frozen, that means that not only is Class
frozen, but so is IInterface
.
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