Make testing (later) easier and your code more flexible with 1 small change

So, if you’re like me from a few years ago, you know you should be testing, right? Like, everybody’s doing it, and you’re totes not a real coder if you’re not.

But you’re not, right? I wasn’t. (Maybe I’m still not; I’m not telling.) But you still want to write software “correctly”, and besides, you’re definitely going to come back later & write some tests for this code. Sure…

And you’ve come across dependency injection & how it’s “the right way”, but WTF does that mean, and why should you care? Great question; please continue reading.

Dependency injection: defined-ish

I looked up the Wikipedia definition here, and I immediately fell asleep. But – I’m back! And maybe I can be helpful.

When you’re practicing OOP, your system has to create objects at some point. If the dependencies of your objects – where “dependency” is the term for an object that your object interacts with – are passed into your objects from some other god-like thing, then you’re using dependency injection.

If, on the other hand, your object is creating its own dependencies, you’re not. If your object uses some other god-like thing to “resolve” or “locate” (read: “find”) its dependencies, you’re not.

Do you prefer code examples? Here is dependency injection:

class Foo
{
    private $dependency;

    public function __construct(InjectedDependency $dependency)
    {
        /**
         * This is dependency injection because whoever called this
         * constructor just poked that ol' dependency right up in here.
         */
        $this->dependency = $dependency;
    }

    public function bar()
    {
        /**
         * And later, we use the thing
         */
        $this->dependency->baz();
    }
}

And this is not dependency injection:

class Foo
{
    private $dependency;

    public function __construct()
    {
        /**
         * This is not dependency injection because this class is
         * responsible for creating its own dependency.
         */
        $this->dependency = new ThingConstructedFromTheConstructor();
    }

    public function bar()
    {
        /**
         * And later, we use the thing
         */
        $this->dependency->baz();
    }
}

Can you dig it? (Note: I’m only showing “constructor dependency injection” here and ignoring the reality of “setter dependency injection”, “service location”, and other possibilities. Curious? Leave a comment.)

How does that help though?

Well, as the title suggests, there are 2 main benefits to dependency injection:

  1. It makes your code more flexible.
  2. It makes testing your code easier.

Wait, actually … well, that’s 1 benefit. In fact, dependency injection makes your code easier to test because it’s more flexible.

Let’s take an example

Assuming that there exists a helpful fellow name Joe Wallace in the world and that he wants to help you, we could easily say that Joe needs to share an idea with you. Now, in the realm of the Internet, there are several means of communication. Joe, naive, selects Twitter to propagate his helpful message. Maybe Joe, in code, looks like this:

class Joe
{
private $meansOfCommunication;

public function __construct()
{
$this->meansOfCommunication = new Twitter;
}

public function shareAnIdea($idea)
{
$this->meansOfCommunication->share($idea);
}
}

This is great! A Joe can just share his ideas on Twitter without fact-checking or anything?!? Fantastic!

But – in reality – Joe has a problem. He can’t help making ridiculous third-person metaphors where he’s actually a block of code instead of a human, and – at some point, many words ago – he greatly exceeded the number of characters allowed in a tweet.

What do we do? We use dependency injection! Instead of allowing the Joe to create his own Twitter as a means of communication, we give him a blog.

class Joe
{
private $meansOfCommunication;

public function __construct($means)
{
$this->meansOfCommunication = $means;
}

public function shareAnIdea($idea)
{
$this->meansOfCommunication->share($idea);
}
}

Savvy readers will notice that we, in fact, did not explicitly give Joe a blog here. We gave him a means of communication, which gives us much greater flexibility.

So – when the god-like things in your system create a Joe object, they have options now!

/**
* Option 1: Twitter
*/
$twitter = new Twitter;
$joe = new Joe($twitter);

/**
* Option 2: Blog
*/
$blog = new Blog;
$joe = new Joe($blog);

“You haven’t said anything about testing, really”

True. Let’s go back to the Twitter thing. If you wanted to test that the Joe object was correctly interacting with Twitter, you could run a test that says, effectively, “Post the tweet ‘foo bar'”, and then you could log into Twitter & check for the tweet with the text ‘foo bar’. But your followers are going to hate you, and that test is sloooooow.

Another option would be to create a totally fake means of communication – maybe called Tweedar – and then check that Joe passed ‘foo bar’ to the Tweedar. Code?

class Tweedar
{
public function share($idea)
{
if ($idea !== 'foo bar') {
throw new Exception('This would be a failed test.');
}
}
}

$tweedar = new Tweedar;
$joe = new Joe($tweedar);
$joe->shareAnIdea('foo bar');

In practice, you’ll use a testing framework like PHPUnit, but the above is an actual, real software test that was enabled because of the architectural decision of “dependency injection”. So that’s a good thing.

In summary

Dependency injection is a simple way to give greater flexibility to your software. With this greater flexibility, you can do more stuff – like write tests, which are a good thing that you’ll totally do later.

And – to be honest – it’s so easy to set up that you should start today. Future you will be grateful.


Did this post make you awesomer?

Sign up for my mailing list to be awesomer oftener.