How to add a specific implementation of a Mock created with Autofixture?
Asked Answered
H

3

5

I am writing tests for class (lets call it Sut) which has some dependencies injected via constructors. For this class I have to use the constructor with the most parameters, therefore I used the AutoMoqDataAttributeGreedy implementation:

public class AutoMoqDataAttribute : AutoDataAttribute
{
    public AutoMoqDataAttribute() : base(new Fixture().Customize(new AutoMoqCustomization()))
    {
    }
}

public class AutoMoqDataAttributeGreedy : AutoDataAttribute
{
    public AutoMoqDataAttributeGreedy() : base(new Fixture(new GreedyEngineParts()).Customize(new AutoMoqCustomization()))
    {
    }
}

The constructor of my sut looks like this:

public class Sut(IInerface1 interface1, IInterface2 interface2, IInterface3 interface3)
{
    Interface1 = interface1;
    Interface2 = interface2;
    Interface3 = interface3;
}

One example test looks like this:

[Theory, AutoMoqDataAttributeGreedy]
public void SomeTest([Frozen]Mock<IInterface1> mock1 ,
                      Mock<IInterface2> mock2, 
                      Sut sut, 
                      SomOtherdata data)
{
    // mock1 and mock2 Setup omitted

    // I want to avoid following line
    sut.AddSpeficicInterfaceImplementation(new IInterface3TestImplementation());

    sut.MethodIWantToTest();

    //Assert omitted 
}

The problem is that I need a specific implementation of IInterface3 for testing and I want to avoid adding a method to my SUT (Interface3TestImplementation) only for my unit test and I also I want to avoid repeating code since I have to add this instance in each and every test.

Is there a nice and neat way to have this implementation being added for all my test / for specific tests with Autofixture?

Heterogamete answered 14/1, 2016 at 11:23 Comment(0)
D
5

If you need to do this as a one-off test, then the answer by Enrico Campidoglio is the way to go.

If you need this as a general rule throughout all of your unit tests, you can customize the Fixture with a TypeRelay:

fixture.Customizations.Add(
    new TypeRelay(
        typeof(IInterface3),
        typeof(IInterface3TestImplementation));

This will change fixture so that, whenever IInterface3 is needed, an instance of IInterface3TestImplementation will be created and used.

Depilate answered 14/1, 2016 at 19:28 Comment(3)
Thanks, I actually need it for most of all my unit test but I like Enrico's solution which would reduce the code in the actual unit test body.Heterogamete
@Heterogamete You can put the above in a Customization that you add in your AutoMoqDataAttribute constructor...Depilate
D'oh... I somehow did not think of that. Thanks again.Heterogamete
R
5

Using your IFixture that you have created, you can call .Register against a specific interface and supply the object to use when that interface is then subsequently used.

e.g.

_fixture = new Fixture().Customize(new AutoMoqCustomization());
_fixture.Register<Interface3>(() => yourConcreteImplementation);

You could also use mocking that would allow you to then use .Freeze on the fixture and that way you could just set some expected calls against the interface and wouldn't need a completely concrete instance. You could let AutoFixture create a default implementation for you and apply the setup that you configured.

e.g.

var mockedInterface = _fixture.Freeze<Mock<Interface3>>();
mockedInterface
    .Setup(x => x.PropertyOnInterface)
    .Returns("some value");
Revive answered 14/1, 2016 at 12:37 Comment(3)
Unfortunately I do need a concrete implementation for Interface3. I also do not understand your first suggestion since I do not create any IFixture. I am using AutoFixture to auto create all my mocks and my sut via the AutoDataAttribute.Heterogamete
This line here in your code will return an IFixture that you can then use. new Fixture().Customize(new AutoMoqCustomization()Revive
Oh ok I thought you were referring to my example above where I do not create a IFixture "in code". The Problem is that I am creating the mocks and Sut via the xUni` Theory as a parameter in the actual Unit test.Heterogamete
D
5

If you need to do this as a one-off test, then the answer by Enrico Campidoglio is the way to go.

If you need this as a general rule throughout all of your unit tests, you can customize the Fixture with a TypeRelay:

fixture.Customizations.Add(
    new TypeRelay(
        typeof(IInterface3),
        typeof(IInterface3TestImplementation));

This will change fixture so that, whenever IInterface3 is needed, an instance of IInterface3TestImplementation will be created and used.

Depilate answered 14/1, 2016 at 19:28 Comment(3)
Thanks, I actually need it for most of all my unit test but I like Enrico's solution which would reduce the code in the actual unit test body.Heterogamete
@Heterogamete You can put the above in a Customization that you add in your AutoMoqDataAttribute constructor...Depilate
D'oh... I somehow did not think of that. Thanks again.Heterogamete
A
4

You can have AutoFixture create an instance of a concrete type and tell it to use that instance every time it has to provide a value for any of its implemented interfaces. Here's an example:

[Theory, AutoMoqDataAttributeGreedy]
public void SomeTest(
    [Frozen]Mock<IInterface1> mock1,
    [Frozen]Mock<IInterface2> mock2,
    [Frozen(Matching.ImplementedInterfaces)]IInterface3TestImplementation impl3,
    Sut sut)
{
}

In this case, AutoFixture is going to create an instance of IInterface3TestImplementation and use it every time it encounters an interface implemented by that type.

This means that if the constructor of Sut has a parameter of type IInterface3, AutoFixture is going to pass it the the same instance that's being assigned to the impl3 parameter, which you can use in your test.

As an aside, there are other ways of matching frozen instances to types and members other than just by interface. If you want to know more, take a look at the members of the Matching enumeration.

Arnoldarnoldo answered 14/1, 2016 at 17:15 Comment(1)
Thanks that was exactly what I was looking for.Heterogamete

© 2022 - 2024 — McMap. All rights reserved.