Dependency Injection as a Tool for Testing

Phil Bennett
Dec 7, 2016 · 4 min read
Image for post
Image for post

Dependency injection is a simple design pattern that provides several benefits, one of which is the ability to vastly improve your unit test suite. In this post I am going to be looking at how this is done and how it relates to certain testing methodologies.

Originally posted 15 August 2013.

Please be aware that all code samples are massively simplified for the purpose of this post and do not necessarily follow best coding practices.

Unit Testing

“Unit testing is a method by which individual units of source code, sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures, are tested to determine if they are fit for use. Intuitively, one can view a unit as the smallest testable part of an application.”

In PHP I like to view a unit as a method of a class. This allows me to run one or multiple unit tests on each method that I write, and to write the code to have a single responsibility and to follow the UNIX philosophy of “Do one thing, and do it well.”.

There are other types of testing that should be employed alongside your unit tests, but for the purpose of this post I will only be looking at unit tests.

A Unit

It is impossible to unit test this code without allowing the doSomething() method to instantiate the real object Bar . As soon as we instantiate the concrete implementation of a class, the test is no longer only testing this unit of code, and therefore we have increased the amount of places that the test can fail.

If we follow the definition of unit testing, this unit is not the “smallest testable part” of our application.

We can refactor the method to receive its dependency Bar as an argument and therefore inject it at runtime instead of instantiating it.

Dependency injection as a design pattern is that simple, now we can inject Bar as an argument of our method. In testing terms, this means that we can now verify state on Bar after the method is invoked.

This is still quite complicated and requires us to access a database or at the very least an abstraction of the database.

Martin Fowler describes the unit we are testing as the System Under Test and any dependencies that we interact with as Collaborators. By abstracting any collaborators, we are able to simplify the process and minimise the places where our test can fail. I want to know that if a test fails, it fails in the System Under Test.

Test Doubles

  • Fake: A fake is a simpler implementation of a real object. For example, it may access an array of data rather than touching a database
  • Stub: A stub is used for providing the tested code with “indirect input” without any implementation. For example, the tested code may rely on invoking a method of another object that will return required data, this is “indirect input”.
  • Mock: A mock is used to verify “indirect output”. For example, the tested code may be required pass arguments to a method of another object, this is “indirect output”.

Testing with a Fake

By injecting a Fake we have isolated the testing to the system under test and we simply verify state on the collaborator to ensure it has changed as expected. Separate unit tests of the real Bar object will handle the testing of the real implementation.

Testing with a Stub

By using a stub, we have provided a hard coded return value from our collaborator. We have also eliminated the need to verify state on our collaborator. This is a pure unit test. We provide the “indirect input” to be returned by the collaborator and only test the code in the system under test. Again, we are relying on the real implementation of Bar to have its own unit tests.

Testing with a Mock

The difference here as opposed to the stub is that we are telling the object to verify that the method is invoked only once, and that the method is passed specific arguments. The mock will verify these conditions are met and throw an exception if they are not. This is essentially verifying state on the collaborator with “indirect output”.

And I’m Off

philipobenito

Just a web log.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store