2

So I have the following class, which takes, in its constructor, three different implementations of the same interface as dependencies:

public class MyTestClass : ISomeInterface<string>
{
    // Constructor
    public MyTestClass([Named("ImplementationA")] IMyTestInterface implementationA,
                       [Named("ImplementationB")] IMyTestInterface implementationB,
                       [Named("ImplementationC")] IMyTestInterface implementationC)
    {
        // Some logic here 
    }

    public void MethodA(string)
    {
    }
}

When using this class outside of unit tests, I inject the dependencies in question with Ninject, i.e. I have something like this:

public class MyNinjectModule : NinjectModule
{

    public override void Load()
    {
        this.Bind<IMyTestInterface>().To<ImplementationA>().InRequestScope().Named("ImplementationA");
        this.Bind<IMyTestInterface>().To<ImplementationB>().InRequestScope().Named("ImplementationB");
        this.Bind<IMyTestInterface>().To<ImplementationC>().InRequestScope().Named("ImplementationC");
    }
}

Which works fine, but the problem I am having now is that I want to unit test this class and I want to do it with AutoFixture, which leads me to my question, how do I create an instance of MyTestClass with those three specific implementations of IMyTestInterface, i.e. ImplementationA, ImplementationB, and ImplementationC? If I just do something like this:

private ISomeInterface<string> testInstance;
private Fixture fixture;

[TestInitialize]
public void SetUp()
{
    this.fixture = new Fixture();

    this.fixture.Customize(new AutoMoqCustomization());
    this.testInstance = this.fixture.Create<MyTestClass>();
}

AutoFixture creates an instance of MyTestClass, but with some random implementations of IMyTestInterface, which is not the dependencies I want. I've been searching for an answer online and the only thing I've found so far that seems similar to what I need, but not quite, is this

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
lukegf
  • 2,147
  • 3
  • 26
  • 39
  • It looks like you can inherit from `ISpecimenBuilder` and create each of the specific interface implementations using AutoFixture: [See this answer](https://stackoverflow.com/questions/26149618/autofixture-customizations-provide-constructor-parameter) and [this answer](https://stackoverflow.com/questions/16819470/autofixture-automoq-supply-a-known-value-for-one-constructor-parameter/16954699#16954699) – Jonathan Tyson Aug 04 '17 at 18:14

2 Answers2

2

AutoFixture was originally built as a tool for Test-Driven Development (TDD), and TDD is all about feedback. In the spirit of GOOS, you should listen to your tests. If the tests are hard to write, you should reconsider your API design. AutoFixture tends to amplify that sort of feedback, so my first reaction is to recommend reconsidering the design.

Concrete dependencies

It sounds like you need the dependencies to be specific implementations of an interface. If that's the case, the current design is pulling in the wrong direction, and also violating the Liskov Substitution Principle (LSP). You could, instead, consider refactoring to Concrete Dependencies:

public class MyTestClass : ISomeInterface<string>
{
    // Constructor
    public MyTestClass(ImplementationA implementationA,
                       ImplementationB implementationB,
                       ImplementationC implementationC)
    {
        // Some logic here    
    }

    public void MethodA(string)
    {    
    }
}

This makes it clear that MyTestClass depends on those three concrete classes, instead of obscuring the real dependencies.

It also has the added benefit of decoupling the design from a particular DI Container.

Zero, one, two, many

Another option is to adhere to the LSP, and allow any implementation of IMyTestInterface. If you do that, you shouldn't need the [Named] attributes any longer:

public class MyTestClass : ISomeInterface<string>
{
    // Constructor
    public MyTestClass(IMyTestInterface implementationA,
                       IMyTestInterface implementationB,
                       IMyTestInterface implementationC)
    {
        // Some logic here     
    }

    public void MethodA(string)
    {    
    }
}

A question that may arise from such a design is: how do I distinguish between each dependency? Most of my Role Hints article series deals with this question.

Three objects, however, is grounds for further reflection. In software design, my experience is that when it comes to cardinality of identical arguments, there are only four useful sets: none, one, two and many.

  • No arguments is basically a statement or axiom.
  • One argument indicates a unary operation.
  • Two arguments indicate a binary operation.
  • More than two arguments essentially just indicates a multitude. In mathematics and software engineering, there are preciously few ternary operations.

Thus, once you're past two arguments, the question is whether or not three arguments don't generalise to any number of arguments? If this is the case, a design might instead look like this:

public class MyTestClass : ISomeInterface<string>
{
    // Constructor
    public MyTestClass(IEnumerable<IMyTestInterface> deps)
    {
        // Some logic here     
    }

    public void MethodA(string)
    {    
    }
}

Or, if you can create a Composite from IMyTestInterface, you could even reduce it to something like this:

public class MyTestClass : ISomeInterface<string>
{
    // Constructor
    public MyTestClass(IMyTestInterface dep)
    {
        // Some logic here     
    }

    public void MethodA(string)
    {    
    }
}

All of these options makes the design clearer, and should also have the derived benefit of making it easier to test with AutoFixture.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • Good points. What I was doing there was an implementation of the Strategy design pattern and the three implementations of IMyTestInterface were different rules that would get invoked based on some condition. What I ended up going with is a version of your Concrete Dependencies idea. Does that not break the Dependency Inversion Principle though? – lukegf Aug 07 '17 at 17:05
  • @lukegf It might, but I don't have all the details. Could you refactor to a Chain of Responsibility instead? – Mark Seemann Aug 07 '17 at 17:19
  • Possibly. I am not too familiar with Chain of Responsibility. Just read-up on it and it looks like it might, but Strategy pattern is a better fit for what I am trying to do. – lukegf Aug 07 '17 at 19:32
1

FWIW, while I believe that my first answer is the best answer, here's a simple way to address the issue with AutoFixture, in case for some reason you can't change the design:

fixture.Register(() =>
    new MyTestClass(new ImpA(), new ImpB(), new ImpC()));

There are other options as well, but none as simple as this one, I believe.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • I did already re-factor my design to use Concrete Dependencies. Also, I had already figure out a way to get AutoFixture to work with my original design, although the suggestion above is indeed simpler. – lukegf Aug 07 '17 at 19:33