Unit Testing made easy with Dependency Injection

Synopsis: An example in which we walk through a case in which Dependency Injection made writing automatic tests, and in particular unit tests, a delight. Which also makes the discipline Test Driven Development much more an option. Find the example used for this post on my GitHub: [9]

Intro

I have introduced Dependency Injection by giving an example in which we show some late binding [1]. After that I’ve showed a possible route to take if certain dependencies depend themselves on run-time values [2]. And there’s an article about how to apply Aspect Oriented Programming (AOP) by using Dependency Injection’s Interception [3]. Now I’d like to talk about how Inversion of Control (IoC) via Dependency Injection (DI) helps you write better unit tests and eventually apply Test Driven Development (TDD). But before we can do that I feel we should first talk about automated tests itself. Because these are crucial for doing healthy TDD. In particular I noticed some confusion about integration tests and unit tests. So what is the definition of a unit test? What is a good unit test? And why does this difference matter so much?

Side note: If you don’t care about TDD, it is perfectly fine to focus on the fact that writing automated tests (unit tests in particular) is very easy if you apply DI.

Integration Test versus Unit Test

To get a good grip on this I think we should compare integration tests with unit tests.

Integration Test

Most automated tests are actually integration tests. It doesn’t matter if you used MS Test’s Unit Test item template. Whether a test is an integration test or not is actually about what is tested factually. If the ‘unit of work’ under test actually spans a couple of components working together, often including external components like a database, a file, a web service, etc., to test if they all play their part, then it is an integration test. An integration test is allowed to be slow, due to its nature. That is why often they are run during a nightly build or something similar. Figure 1 illustrates connected code components which would have to be tested via an integration test with long running processes like calling a web service, persisting data in the database and writing data to a file on the hard drive:

Figure1IntegrationTest

Figure 1: Integration testable

Unit Test

Tests that define a  clear ‘unit of work’, without any external component involved, and just enough components to test that what must be tested, is an unit test. To emphasize the remark about external components; a unit test can’t extend to persistence layers like databases, or files on disk, or web services somewhere on the internet. And if too much components are involved, the test does too much and can’t be read easily. Unit tests are mainly for teams of developers to keep proving to themselves that the code still works as intended. Components not directly involved in the current test need to be faked (stubbed or mocked). Don’t worry if some terms are not familiar. I am working on an in-depth article about unit testing and TDD, but if you read “The Art of Unit Testing” by Roy Osherove [4] you will know everything you need to know and more! He has for instance an extensive (and much better) definition of a unit test on his website [5]. Figure 2 illustrates with red crosses where the connections in figure 1 would need to be severed to make it unit testable:

Figure2UnitTest

Figure 2: Unit testable

So why does this matter?

Unit Tests need to be fast, concise and easy to read. In an Agile Scrum environment for instance where you need to release a potentially shippable increment at the end of every sprint you need to maximize your feedback loops. You want to know on codebase level at every moment if everything is still in order. That is why a Scrum team should strive for automated unit tests that fire off with every source control check in. And that is why they need to be fast. Because if they are slow you don’t want to fire them off with every check in. And because they need to be fast and need to tell you at a glance where something is wrong, they need to be small and concise. Because they are small and concise you will have a lot of tiny unit tests. And the Scrum team will need to maintain them. If unit tests are not readable they will be ignored and poorly maintained, effectively killing off the effect of unit testing. I’ve summarized this in figure 3:

Figure3MaximizeFeedbackloop

Figure 3: Maximize feedbackloop

Now that I’ve illustrated the difference it is time to move on to actual code!

Unit test without DI

I started adding (as I thought) unit tests to the solution. It has been proven to be a good practice if you make a separate project for your tests and keep them out of your main project [6]. I started out by using the Unit Test Project template. All this mainly does is add the correct assemblies to start using MS Test basically. Plus the tests show up in Visual Studio’s Test Explorer out of the box. For this example however I’ve used XUnit instead of MS Test (if you are interested in my reasons why you can check out a post at Mark Seemann’s blog [7]). The tests and classes from figure 1 would look like:

Listing1FirstAttemptSomeService

Listing1FirstAttemptSomeRepository

Listing1FirstAttemptSomeClient

Listing1FirstAttemptSomeLogger
Listing1FirstAttempt
Listing 1: First attempt at SomeService and its unit test

So what are the problems with the unit test above? The test takes too long. And it needs to assert too much. If you add up the milliseconds you’ll be looking at 8000 milliseconds in total. Of course fictive and a bit exaggerated for the purpose of this example, but even half of that would be way too much for a simple unit test. This is caused by calls to external components which simply take longer processing time. So as figure 2 showed us we need to sever the connections (read: Dependencies) and actually not write something to the database, or call a web service, or write a log file, if we simply want to unit test DoStuff. To be able to do this we need to stop initializing everything in the DoStuff method. That is where Dependency Injection is going to help us. Listing 2 shows our second attempt with ImprovedSomeService:

Listing2ImprovedSomeService

Listing 2: ImprovedSomeService

And the unit test could be something along the lines of:

Listing3SecondAttempt

Listing 3: Second attempt at unit test with ImprovedSomeService

As you can see in listing 3 Dependency Injection makes it very easy to fake all the components that are not needed in the ‘unit of work’ at hand. In this example the free library FakeItEasy [8] is used to create mocks and stubs. The unit test begins by making some fakes (FakeItEasy makes syntactically everything a fake and the context decides whether it is used as a mock or a stub). These fakes are injected into the service via its constructor. Then the test simply proceeds by asserting if some calls to certain methods actually happened and whether the return value is true from the method DoStuff. This test is focused and concise, plus the time this test takes is not even worth mentioning.

TDD

It should be easy to see via listing 3 how Dependency Injection can make your pain with TDD a little lighter. Let’s throw the familiar diagram on here to provide some context:
Figure4redgreenrefactor

Figure 4: TDD cycle

As you know you write the test first, so imagine we had the requirement to ‘do’ some ‘stuff’. I write the test first, so I try to call a DoStuff method which didn’t exist. This is the red state. I generate the method. The test becomes green. I refactor this by making SomeService class, putting the DoStuff method in there and initializing SomeService to call DoStuff on it in the test. And you repeat the process by wanting to know in the test if DoStuff failed or succeeded. The test is red. DoStuff needs a Boolean return value and we return true in the method. Test is green. During refactoring we could for instance only return true if some operation actually succeeded. This operation could for instance be putting stuff in the database. So in the DoStuff method of SomeService I call WriteStuffToDb on the non-existent class SomeRepository. Making the test become red again. I generate the class and the method. I am not interested right now in the actual implementation of the call, so to make the test green I fake the class and test if the method is called at all. Then I refactor this so the implementation of ISomeRepository is injected through the constructor making it easy for the next iterations to insert dummy repositories. And so on…

Conclusion

I showed you the difference between integration tests and unit tests. Then I showed you how Dependency Injection makes unit testing a lot easier. Finally I brought some TDD into the mix. You can find all the code and more on my GitHub: [9]. This article is also published in Dutch magazine #127 called SDN which you can download here: [10]. I recommend that you do because Pepijn Sitter [11] wrote a cool article about Roslyn’s Analyzers, Gerald Versluis[12] about Xamarin.Forms, and so on. So go check it out.

Side note: Used Ninject as IoC Container because the API is awesome!

Links

  1. Introduction Dependency Injection: https://dannyvanderkraan.wordpress.com/2015/06/15/real-world-example-of-dependeny-injection/
  2. Dependency Injection based on run-time values: https://dannyvanderkraan.wordpress.com/2015/06/29/real-world-example-of-dependency-injection-based-on-run-time-values/
  3. AOP with Interception: https://dannyvanderkraan.wordpress.com/2015/09/30/real-world-example-of-adding-auditing-with-dependency-injections-interception/
  4. Art of Unit Testing: http://artofunittesting.com/
  5. Definition of a Unit Test: http://artofunittesting.com/definition-of-a-unit-test/
  6. Great discussion about seperate projects for tests: http://stackoverflow.com/questions/2250969/should-unit-tests-be-in-their-own-project-in-a-net-solution
  7. Reasons for XUnit: http://blog.ploeh.dk/2010/04/26/WhyImmigratingfromMSTesttoxUnit.net/
  8. FakeItEasy: https://github.com/FakeItEasy/FakeItEasy
  9. GitHub: https://github.com/DannyvanderKraan/DependencyInjectionMakesUnitTestingSimple
  10. SDN 127: http://www.sdn.nl/portals/1/magazine/SDN-Magazine-127.pdf
  11. Pepijn Sitter: https://nl.linkedin.com/in/pepijn-sitter-65514a
  12. Gerald Versluis: https://nl.linkedin.com/in/jfversluis
Advertisements

About Danny

Bachelor in Commercial ICT MCTS Winforms .NET 2.0 MCTS ASP.NET 3.5 PSM I
This entry was posted in .NET programming, Agile Scrum, Dependency Injection, Software design patterns and tagged , , , , . Bookmark the permalink.

2 Responses to Unit Testing made easy with Dependency Injection

  1. Thế Hiển says:

    Thank you so much. Very helpful !

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s