Unit Testing Made Easy – DI part 3

I claimed in a previous post that low coupling using dependency injection made the code base more testable – i.e. properly prepared for unit testing. Let’s dig a bit deeper into that assertion.

The ProductService class is an obvious candidate for unit testing. It is a relatively small component with a well-defined responsibility (adhering to the Single Responsibility Principle). It is also properly isolated from its dependency (the repository), by an abstraction (the interface). Let’s create a unit test method for the ProductService component:

This unit test method verifies that the ProductService functionality for calculating a discounted price of a product works correctly. It follows the standard sequence of a unit test: First it sets up the fixed baseline environment for the test (also called the test fixture). Then it exercises the system under test (in this case the product service). Finally, it verifies the expected outcome. A “tear down” phase is not necessary, as the fixture objects automatically gets out of scope and will be garbage-collected.

As ProductService does not care about the actual implementation of the product repository dependency, you can inject a “stand-in” for this dependency in the test. This stand-in is better known as a test double. The mockRepository variable holds an instance of such a product repository test double.

In the final application you are probably going to implement the repository so that the products are persisted in for example an SQL database, or maybe a file, but the elegant thing is that, at this moment in time, you do not need to care about this. In the context of the unit test, you can just make a mock implementation of the repository which does not implement persistence of the products at all, but just keep them in memory. This is our test double. Obviously, an implementation like this would never make it into the final application, but it is sufficient to test the ProductService functionality in isolation.

Such a mock implementation of a repository is easily done. Of course you make a generic version that can be used as test double for all entity repositories:

A Dictionary object is used to hold the entities in memory during the test.

Testability is not necessarily the main purpose for doing dependency injection, but the ability to replace dependencies with test-specific mock objects is indeed a very useful by-product.

By the way, the unit test method above is written using the xUnit.net testing framework, which explains the Fact attribute and the Equal assertion. xUnit.net is a nice and very lean testing framework – compared to for example the MSTest, which is the one integrated with Visual Studio. With xUnit.net you don’t need to create a specific test unit project. Also, you get rid of the auto-generated .vsmdi files and .testsettings files from MSTest.

To further refine and automate your unit tests, you should consider using supplementary unit test frameworks like AutoFixture and Moq to help you streamline fixture setup and mocking. Both are available from within the “NuGet Package Manager” Visual Studio Extension. I have written a comprehensive CodeProject article about using xUnit.net, AutoFixture and Moq.

3 thoughts on “Unit Testing Made Easy – DI part 3

  1. Very nice and helpful article series.

    What is the reason that the product also has an interface? On the site there are no reason to pass the IProduct-Interface to the MockupRepository and then create a POCO object, right?

    1. Thank you Rudolf. You are right that there is basically no need to have the IProduct interface. However, the reason why it is there anyway is that I used the mocking framework Moq in my unit tests. Moq can create mock objects out of interfaces. Actually the same comment was made on an article I made for CodeProject 🙂 In that article, and in the sample code, I removed the interface. See my article Lightweight Entity Services Framework.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.