In one of my previous posts I showed some code calling the Team Foundation Server. During test runs I had a reference to our production TFS box. You can imagine that carefull testing was the first law to obey, hence the built-in option 'Emulate' to not really destroy anything.
For Unit testing this approach was somewhat cumbersome so I needed a better approach. My first class model looked like this:
In this model I have two dependencies on two external types in the assembly Microsoft.TeamFoundation.Build.Proxy. During Unit testing I want to call an instance of those classes that have the same signature. NMock provide such functionality. It simulates an actual class. To instantiate an Mock class you need to provide it an interface. Time for some refactoring.
As you can see the Action classes now depend on the new Interface ITeamFoundationServer. The class TeamFoundationServer implements that interface and has dependencies on the TFS classes. The Action classes obtain an instance to an implementation of ITeamFoundationServer by calling the static property Instance on the TeamFoundationServer class. So this class also acts as a Factory.
Mock
When we are running a unit test we want to provide our own implementation of the ITeamFoundationServer interface. For this to happen we need a form of dependency injection. As I do not want to introduce a complete, full blown dependency injection framework (I could have used ObjectBuilder), I simply provide a dependency injection constructor on the TeamFoundationServer class. The constructor accepts an ITeamFoundationServer implementation and sets the corresponding private static field _Instance. The default constructor is private which prevents instantiating the TeamFoundationServer class from non-testing code.
After you added a reference to the NMock assembly you can start creating Mocks in your test method. First you start off with creating a Mockery object and then you're ready for creating a mock object for the interface. The instance returned from that call is injected in our code by the means of the dependency constructor as shown in the following snippet:
Mockery mock = new Mockery();
ITeamFoundationServer itfsMock = (ITeamFoundationServer) mock.NewMock(typeof(ITeamFoundationServer));
TeamFoundationServer tfsDummy = new TeamFoundationServer(itfsMock);
The next step is to define what call will be expected on the interface and which return values should be delivered in that case. For example:
Expect.Once.On(itfsMock).Method("GetListOfBuilds").With(new object[] {"par1", "par2"}).Will(Return.Value(TestData()));
This tells the NMock framework to expect one call to GetListOfBuilds with two string parameters. That call will return testdata. The testdata is choosen so that it exercises different execution paths to maximize codecoverage. Now you're ready to make the call to your class/classes under test. As the call returns one final call to the mock framework will check that all of our expected calls have been taking place.
mock.VerifyAllExpectationsHaveBeenMet();
We now have a unit test that can safely run without the need for an up-and-running teamfoundationserver. So everybody can relax...
CodeCoverage
The best thing of the mock object is the flexibility in accepting and returning data for testcases that would require running complex database scripts or infrastructure. You can even throw exceptions. Having said that there is no reason for a codecoverage of less than 100%.
If you have no ambition you're close to death. It is however hard to reach 100%. In my case the real implementation was also taken into account for the coverage and was off-course not covered by any test. In the first run only 60% of the code was covered. By adding more testcases (and richer testdata), refactor out common code and having the mock framework throw excpetions I achieved over 80%.
To reach the 90% I needed some trickery. Inspection of the non-coveraged code blocks revealed that some private constructors were not called. With some reflection magic that can be solved:
Type tfsType = typeof(TeamFoundationServer);
ConstructorInfo[] call = tfsType.GetConstructors(BindingFlags.NonPublic | BindingFlags.CreateInstance | BindingFlags.Instance);
object tfsInstance = call[0].Invoke(null);
Also the Dispose method was not fully covered because some internal fields where never set. Also for this CodeCoverageCase some reflection magic was needed. This is how you achieve that. Pay attention to the fact that this field is private so it can not be set from the testcode.
tfsInstance.InvokeMember("_Instance",
BindingFlags.Instance | BindingFlags.SetField | BindingFlags.NonPublic,
null,
tfsInstance,
new object[] { tfsInstance });
Calling Dispose now did exercise all code paths.
Conclusion
If you're serious about Unit testing you need to design for testability. A good test design enables the Unit test to insert Mock objects without cluttering you're code with test artifacts. Using Mock objects gives you the ability to test even complex sceanrio's relatively simple. Using Code coverage to measure the effectiveness of your unit tests is a best practise. To achieve top code coverage ratings you might need some magic.