Debunking 2 major myths about dependency injection containers

There’s been lively conversations over the use and merits of dependency injection (DI) containers over the years. I’m a pragmatic software developer. I try doing what works best for any given situation. There are instances where I believe DI containers are of value and there are instances where I believe they are misused.  Here are two major misconceptions leading to what I believe is an improper use of DI containers.

  • DI containers improve testability
  • DI containers reduce coupling

DI containers improve testability

This is very common reason given when people advocate the use DI containers. The theme here is that by using a DI container one can easily swap out dependencies for mocks and stubs.  While this is true it’s also equally true that this can be done without using a DI container.

Let’s take a look at a typical DI container scenario.  The following code will use the Unity DI container and the Moq framework.

public interface IOrderRepository
{
    decimal GetOrderAmount(int orderId);
}
public class OrderRepository : IOrderRepository
{
    public decimal GetOrderAmount(int orderId)
    {
        throw new NotImplementedException();
    }
}
public class CustomerService
{
    public IOrderRepository OrderRepository { get; set; }
    public CustomerService(IOrderRepository repository)
    {
        OrderRepository = repository;
    }
    public decimal GetOrderAmountDue(int orderId)
    {
        var orderAmount = OrderRepository.GetOrderAmount(orderId);
        return ApplyTaxes(orderAmount);
    }
    private decimal ApplyTaxes(decimal amount)
    {
        //...
    }
}

[TestMethod]
public void CanGetAfterTaxesAmountDue()
{
    UnityContainer container = new UnityContainer();
    var mock = new Mock<IOrderRepository>();
    mock.Setup(m => m.GetOrderAmount(0)).Returns(20);
    container.RegisterInstance<IOrderRepository>(mock.Object);
    var service = container.Resolve<CustomerService>();
    var amountDue = service.GetOrderAmountDue(1);
    Assert.AreEqual(22.5m, amountDue);
}

And now a similar sample without using a DI container

public class CustomerRepository
{
    public virtual decimal GetBalance(int custId)
    {
        throw new NotImplementedException();
    }
}
public class InsuranceService
{
    public CustomerRepository CustomerRepository { get; set; }
    public InsuranceService(CustomerRepository repository)
    {
        CustomerRepository = repository;
    }
    public decimal GetCoverageAmount(int custId)
    {
        var balance = CustomerRepository.GetBalance(custId);

        return CalculateCoverageAmount(balance);
    }
    private decimal CalculateCoverageAmount(decimal amount)
    {
        //...
    }
}
[TestMethod]
public void CanGetCoverageAmount()
{
    var mock = new Mock<CustomerRepository>();
    mock.CallBase = false;
    mock.Setup(m => m.GetBalance(0)).Returns(20);
    var service = new InsuranceService(mock.Object);
    var coverageAmount = service.GetCoverageAmount(1);
    Assert.AreEqual(12.5m, coverageAmount);
}

These samples are very simple, however this same technique can be applied to more complex scenarios as well.  The basic principle behind this technique is simple.

  • Mock classes using polymorphism
  • Inject the mocked objects into the class being tested

I used a mock framework in these samples, but it’s not required.  I could have created a mock class that implements IOrderRepository in scenario 1. For scenario 2 I could have created a mock class that extends CustomerRepository.

DI containers are sometimes used for their service locator capabilities. A fairly common scenario occurs when putting legacy code under test.  Take this following class.

public class CustomerManager
{
 ...
 public IEnumerable<int> GetOrderNumbers()
 {
  return new OrderRepository().GetOrders().Select(o=>o.Id);
 }
}

If the task is just to put CustomerManager under test and changing any outside code isn’t an option at the moment.  Dependencies can easily be injected with a DI container by doing the following.

public class CustomerManager
{
 ...
 public UnitContainer UnityContainer {get;set;}
 public IEnumerable<int> GetOrderNumbers()
 {
  return UnitContainer.Resolve<OrderRepository>().GetOrders...
 }
}

This can also be accomplished without a DI container using any of the creational patterns.  It can be as simple as the following.

public class CustomerManager
{
 ...
 public Func<OrderRepository> OrderRepositoryProvider = () => new        OrderRepository();
 public IEnumerable<int> GetOrderNumbers()
 {
  return OrderRepositoryProvider().GetOrderAmount();
 }
}

The unit test just needs to change the “provider” and the task is complete.

I can go through a number of different scenarios, from my experience I can confidently claim that DI containers do not improve testability.

DI containers reduce coupling

It is said that DI frameworks reduce coupling by removing dependencies to concrete implementations.

We know this isn’t true as a rule since DI frameworks can resolve concrete classes.  I will also state that that there are cases where a DI container can reduce coupling.  For example, when you add a new dependency to class constructor, the calling code will not need to change in order to add this new dependency.  There are drawbacks to this as well.  These problems are not really the issue I have with the claim that DI containers reduce coupling.  My issue with the claim is two fold

  • Developers create abstractions where they are not needed
  • Having a class depend on an interface does not mean coupling has been reduced in the application.

The first point is a result of developers who misinterpret what an abstraction is.  An abstraction is not necessarily an abstract class, or an interface. Developers end up creating abstractions for things that are not actually abstract or have already been abstracted. The second point is that developers sometimes remove a dependency from a library that is only ever used in one application.  So while the library no longer has the dependency, the application still does. Removing the dependency from the library has accomplished nothing.  Let me use a few examples to clarify.

public class ChargeService
{
 ...
 public int GetChargeId(string chargeToken)
 {
  return (int)CreateSelectChargeIdCommand(chargeToken)
   .ExecuteScalar();
 }
 public SqlCommand CreateSelectChargeIdCommand(string chargeToken)
 { ... }
}

The ChargeService has a dependency on sql server.  If we want to switch database engines the ChargeService class will need to change.  To avoid this we create an abstraction.  We accomplish this by doing the following.

public class ChargeRepository
{
 public int GetChargeId(string chargeToken)
 {
  return (int)CreateSelectChargeIdCommand(chargeToken)
    .ExecuteScalar();
 }
 public SqlCommand CreateSelectChargeIdCommand(string chargeToken)
 { ... }
}
public class ChargeService
{
 ...
 public ChargeService(ChargeRepository repository)
 {
  ChargeRepository = repository;
 }
 public void ChargeCustomer(string chargetoken)
 {
  var chargeId = ChargeRepository.GetChargeId(token);
 }
}

Now that I have abstracted the calls for the database engine. If I need to change database engines the ChargeService class does not need to change, I only need to change the repository class.  I didn’t need an interface to accomplish this.

There are many cases where an interface should be used for these types of abstractions.  I’m thinking of cases like writing a library that will be used in applications outside the control of the person writing the library. However in many cases, be it misconceptions or perhaps a slight case of over-engineering, unnecessary complexity and time of development have been added to projects.

That concludes my contribution to the dependency injection discussion.  I hope to contribute more in the future by sharing scenarios where I advocate the use of dependency injection containers.  In the meantime feel free to leave a comment.  I look forward to exchanging ideas.

 

8 thoughts on “Debunking 2 major myths about dependency injection containers”

  1. Because you CAN mock a concrete class for the sake of unit testing, does not mean it’s a good practice or rule of thumb. In using a concrete class, you now have created a ‘hidden’ requirement that in order to write the unit test, you better both mock and inject this mock in place of the repository or you will running it against functional code you didn’t necessarily intend to test. When you use the abstraction thru an interface, the test could not possibly run successfully without first mocking the abstraction and setting up any called methods/functions/properties being used during the test. So instead of a ‘hidden’ requirement, you now have an ‘explicit’ requirement.

    1. It’s a good point and I do run into this scenario. I would say in most cases the result is more test coverage and I am perfectly fine that. If a class is difficult to mock I’ll just mock more of it. Pretty rare that I would revert to an interface just so that I can support my unit test.

  2. “Having a class depend on an interface does not mean coupling has been reduced in the application.”

    By it’s very definition it does in fact reduce coupling. It’s impossible for it not to have reduced coupling if your suggested alternative is a concrete class. In your example with the ChargeService, you’ve now coupled your ChargeService with the ChargeRepository and whatever particular database engine it is using. Therefore, when you go to use your ChargeService and it’s logic in other applications, you are necessarily requiring that those applications support whatever database engine you happened to pair with your ChargeRepository. You couldn’t for instance, simply re-use your charge service for a web-app that happens to need to use the PayPal api (for example). What then usually happens is the developer ends up creating a new overload of the constructor that takes something like ApiChargeRepository. Which now couples you further to the ApiChargeRepository and whatever underlying engine underlies it. Soon your service is bloated and not very agile.

    1. In the blog I state that while you have reduced coupling on the library or class, if that class or library is only used by the one application you are working on, your application still has the same coupling to that underlying concrete class. If your application or library is going to be reused that is a different story.

      To support multiple payment processors I would certainly use an interface.

      Thanks for the comments Nicholas.

  3. I disagree with most of this article.

    First, you immediately turn back on your attempt to “debunk” these “myths”. You admit in the first paragraph that “by using a DI container one can easily swap out dependencies for mocks and and stubs”. That improves testability, which you claim to be the first “myth”. Later, when you talk about the reduced coupling “myth”, you admit that DI containers DO reduce coupling and then immediately make a completely different argument about over-abstraction.

    The approach to avoiding DI containers (in the testability portion) involves unnecessarily modifying the logic which is actually being tested (i.e. the “SUT”, or System Under Test), by tagging methods as “virtual” for no reason other than test logic. Modifying a concrete class for that reason alone is generally bad practice and should be avoided where possible.

    Also, why did we need to ditch the interfaces in the first place? One can achieve the same result WITHOUT a DI container and STILL USING the interface portion of the example implementation. In fact, I don’t understand how this section actually pertains (at least directly) to the use of DI containers. Rather, it seems like an argument on whether to use interfaces as your dependencies which is a separate topic entirely. It makes your example convoluted since you are tackling multiple, mutually exclusive “problems” at once – (1) “Mock classes using polymorphism” and (2) “Inject the mocked objects into the class being tested”. The former recommendation has nothing to do with the topic at hand and does nothing to help resolve the stated issue or illustrate your point.

    Regarding your comment that, “DI containers are sometimes used for their service locator capabilities”, I’m shocked. One of the strongest arguments to use DI containers is to get rid of service locator patterns. How do you blame the pattern that FIXES the problem as the CAUSE of the problem in the first place? This entire point seems invalid.

    To further drive that point home, regarding your comment that, “This can also be accomplished without a DI container using any of the creational patterns. It can be as simple as the following.”, this is how he should have done it WITH a DI container in the first place. The dependency is on a function used to retrieve an OrderRepository. You can configure a DI container to inject a Func (or Func) which has been configured as a “real” factory method or a “fake” (which would return a mock instead of the “real” implementation). The first example is just wrong for any implementation, and shouldn’t be used to detract from DI containers.

    In general, I understand that we shouldn’t credit DI containers with things they don’t accomplish, and that DI containers are not a requirement for testable classes (by a long shot) . Unfortunately, I disagree with every argument you made trying to get there.

    1. My point is that containers are sometimes misused on the false premise of testability and coupling. Unnecessary complexity and effort is introduced while simpler alternatives were available. The blog focuses very much on unnecessary abstractions and testing techniques that do not depend on interfaces.

      Adding the virtual keyword is a very small effort as opposed to introducing interfaces and dependencies to a DI/IOC framework. In Java, every method is virtual by default. I don’t lose much sleep over adding virtual on methods I want to mock/stub.

      On the topic of service location, every DI/IOC framework I have worked with has a service locator implementation. As for it’s use, I bet you use it at the composition root of your services. How many times have you seen a class with two constructors, one that takes in dependencies and another that instantiates them via service locator?

      I discussed the service locator pattern in the context of a legacy system. If you have had the pleasure of working with code bases that are 15 to 20 years old you can appreciate that not everything is coded nice and tidy like we would like it. We need to be pragmatic. The example I referenced in the blog mentions a task where you are to put a class under test without modifying the calling code (this is a very good way of refactoring legacy systems, put the system under test first).

      1. What modification was necessary to the calling code in order to achieve the DI implementation? That’s not apparent.

        I have spent a good deal of time with legacy code over a decade old, but don’t understand the relevance here. In both of your examples, you are demonstrating how to architect both the calling class and the dependency class. I didn’t catch any restrictions on what could be changed and don’t see any outstanding restrictions inherited by one implementation versus the other.

        Lastly, I have never seen someone overload a constructor with a version that pulls from service locator, but I have seen people just “new” up a dependency in that scenario. I’ve also seen service locator used to initialize “private readonly” fields directly (same line). I understand why it’s sometimes tolerated. What I don’t understand is how it impacts either of the DI container myths, as advertised.

Leave a Reply

Your email address will not be published. Required fields are marked *