If you’ve never done Test Driven Development or aren’t even sure what this "crazy TDD stuff” is all about than this is the series for you. Over the next 30 days this series of posts take you from “I can spell TDD” to being able to consider yourself a “functional” TDD developer. Of course TDD is a very deep topic and truly mastering it will take quite a bit of time, but the rewards are well worth it. Along the way I’ll be showing you how tools like JustCode and JustMock can help you in your practice of TDD.
Previous Posts in this Series: 30 Days of TDD – Day 22 – When is a Test Not a Unit Test?
The examples I’ve been working with for this series have assumed that you are doing "Green Field” development. This means that we are writing an application from scratch and can make sure all of the things that make code testable are incorporated into our design from the start. The biggest one of these things being the user of dependency injection. But what if you are working on “legacy” code that still uses static dependencies and not dependency injection? Is TDD and unit testing out of reach due to a decision made at the beginning of the project? Not necessarily.
The First Step is Admitting You Have a Problem
Note: Up till now I’ve been using the free version of JustMock called JustMock Lite. The features I’ll be demonstrating in this post are part of the full commercial version of JustMock. If you do not own this version of JustMock you can download a 30 day trial here.
When practitioners of TDD use the term “legacy code” what they are usually referring to is code that has not been designed to use dependency injection and therefore is difficult to unit test. Not being able to mock the dependencies of a class is certainly a barrier to writing unit tests that isolate your code under test. In fact, tests written against these types of classes and method cannot be considered unit tests; they are integration tests.
Most developers today are working with legacy code. Most developers work at large organizations or enterprises where they work on a team that is dealing with a line of business application whose line of code count could number in the millions. In this situation having a good solid suite of unit tests could be a huge benefit in terms of developer efficiency and application quality. It’s just that million lines of legacy code that are standing in the way. In these cases a mass-refactor of your application to add dependency injection is usually out of the question.
To address the needs of developers in this situation, JustMock has a feature called Future Mocking. Future mocking allows you to mock private statically bound dependencies in your code. For example, let’s consider these two classes:
1: using System;
2: using System.Linq;
4: namespace FutureMocking
6: public class DependencyClass
8: public int GetDependentValue()
10: return 42;
14: public class DependentClass
16: private DependencyClass _dependencyClass;
18: public DependentClass()
20: _dependencyClass = new DependencyClass();
23: public int GetValue()
25: return _dependencyClass.GetDependentValue();
(get sample code)
On line six I’ve created a class I’m calling DependencyClass. On line 14 I have created a class called DependentClass which is dependent on DependencyClass. As you can see on the constructor for DependentClass, which starts on line 18, I am not using dependency injection. DependentClass is statically bound to the DependencyClass by virtue of the instantiation of the DependencyClass on line 20. As I have no way to pass in a mock for DependencyClass as part of my unit test, with most mocking frameworks I can only really write an integration test for this unit of code.
Using Future Mocking
We I used JustMock Lite in my previous examples I was able to add it to my project with NuGet. I won’t be able to use NuGet to get the full version of JustMock. The good news is that by installing it I’ve gained a couple new project types in Visual Studio (Figure 1):
Figure 1 – New project types added by JustMock
Under the Telerik –> Test branch of the new project templates dialog I have two new project types. I now have the ability to create a new JustMock Test project in either C# or VB.NET. By using this new project type I can create a class library that has the appropriate JustMock assembly referenced and I get a few code files showing various examples and boilerplate code that I can use to start writing tests.
If you want to use JustMock in an existing project you can add a reference to the JustMock assembly manually. When you install JustMock the default location of the Telerik.JustMock.dll assembly will be C:\Program Files\Telerik\JustMock.
To test my tightly coupled code I create a new JustMock test project and deleted the exiting files (I don’t need this right now). I then added a C# file called FutureMockingTests.cs and wrote my test:
2: public class FutureMockingTests
5: public void FutureTest()
7: var expectedDependency = Mock.Create<DependencyClass>();
8: Mock.Arrange(() => expectedDependency.GetDependentValue())
12: var classUnderTest = new DependentClass();
14: Assert.AreEqual(5, classUnderTest.GetValue());
(get sample code)
This test should look pretty familiar; I create a mock of the class I want to mock (DependencyClass) and then arrange the mock. I then call the class and assert the results. The difference here is the call to IgnoreInstance on line nine. What this does is tell JustMock to replace all calls to the arranged method (in this case GetDependentValue) with the arrangement I have supplied. This enables me to create mocks for classes that are statically bound without using dependency injection.
Before I run my test I need to ensure that the JustMock profiler is enabled. I can use the JustMock menu in Visual Studio to enable and disable the profiler (Figure 2):
Figure 2 – Enabling the JustMock profiler
Running this test we can see that the replacement of the static method definition is replaced by our arrangement (Figure 3)
Figure 3 – A passing test
Some Things to Consider…
TDD is not only about testability. It’s also about creating well designed applications. This ties in with adherence with the SOLID principles, specifically the Liskov Substitution principle and the Dependency Inversion principle. These in turn tie into the practice of dependency injection. These are all things that are employed to make our applications better designed, more maintainable and more extensible.
Future mocking very much flies in the face of all of this. So why offer it? The reason is that developers who are working with large legacy projects would otherwise have no way to write unit tests for their existing code. Future mocking is only recommended for these “legacy code” situations; it is not recommended that you start a project with future mocking. It’s also recommended that when working with legacy code you consider future mocking a stop-gap practice and should be trying to refactor your code to use dependency injection. It may take years, but your goal should be to wean yourself off of future mocking.
As mentioned previously future mocking uses the JustMock profiler. This profiler enables future mocking, however it can make your tests run noticeably slow. If you do not need any of the JustMock features that require the profiler it is recommended that you disable it.
In an ideal world we would all be working in beautifully designed codebases that make development a simply and joyful experience. Unfortunately that’s not usually the case. In some cases we inherit another developers past problems. By using the future mocking feature of JustMock some of these issues can be mitigated and make unit testing an option in places it would otherwise not be possible.
Continue the TDD journey: