Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dependency Injection: Turtles all the way down?

So I'm wondering about how unit testing works in regards to dealing external dependencies. Here and elsewhere I've become familiar with dependency injection, and how that allows us to test a unit (A) of code. However, I'm confused about how to test other units (B and C) which are now possess the external dependency so they can inject it into the original unit (A).

For example, say some class Foo uses an external dependency...

class Foo
{
    private ExternalDependency ed;
    public int doSomethingWithExternalDependency() {...}
}

And class Bar makes use off Foo...

class Bar
{
    public int doSomethingWithFoo
    {
        Foo f = new Foo();
        int x = f.doSomethingWithExternalDependency();
        // Do some more stuff ...
        return result;
    }
}

Now, I know that I can use dependency injection so that I can test Foo, but then how do I test Bar? I guess, I can, again, use dependency injection, but at some point some unit needs to actually create the external dependency; so how do I test that unit?

like image 250
guy Avatar asked Dec 31 '10 15:12

guy


People also ask

How can dependency injection be resolved?

Resolve dependencies using IServiceProvider You can use the IServiceCollection interface to create a dependency injection container. Once the container has been created, the IServiceCollection instance is composed into an IServiceProvider instance. You can use this instance to resolve services.

What are the three types of dependency injection?

There are three main styles of dependency injection, according to Fowler: Constructor Injection (also known as Type 3), Setter Injection (also known as Type 2), and Interface Injection (also known as Type 1).

What is the difference between dependency injection and inversion?

Dependency Injection is the method of providing the dependencies and Inversion of Control is the end result of Dependency Injection. IoC is a design principle where the control flow of the program is inverted.


1 Answers

The examples you provide do not use Dependency Injection. Instead, Bar should use Constructor Injection to get a Foo instance, but there's no point in injecting a concrete class. Instead, you should extract an interface from Foo (let's call it IFoo) and inject that into Bar:

public class Bar
{
    private IFoo f;

    public Bar(IFoo f)
    {
        this.f = f;
    }

    public int doSomethingWithFoo
    {
        int x = this.f.doSomethingWithExternalDependency();
        // Do some more stuff ...
        return result;
    }
}

This enables you to always decouple consumers and dependencies.

Yes, there will still be a place where you must compose the entire application's object graph. We call this place the Composition Root. It's a application infrastructure component, so you don't need to unit test it.

In most cases you should consider using a DI Container for that part, and then apply the Register Resolve Release pattern.

like image 145
Mark Seemann Avatar answered Oct 08 '22 18:10

Mark Seemann