How partial classes can help Unit Testing
Testing a class for correctness using Unit Tests means to exercise all methods of a class. This is especially easy for public methods. You can freely choose where to locate your test code, it always has access to public methods of the class. But how about internal/friend methods for example? They are only visible to test code located in the same project/assembly as the class to be tested. And how about private methods? And how about methods without any "directly visible effect", i.e. methods which change only the private state of an object of the class to be tested?
I favor separating test code from implementation code. My "component test beds" look like this:
Let´s assume, I´m developing a stack component called "MyStack". I´d then create a Visual Studio solution, e.g. MyStack Component.sln, and realize the component as an assembly (Visual Studio project), e.g. MyStack.csprj. All tests for that componet, though, would go into a separate project, e.g. test.MyStack.csprj. Why´s this? Because then I potentially can reuse the tests for different implementations. However, this depends on defining an interface/contract for the component and requires the test to dynamically load an implementation.
Although the above example does not define a separate interface for the component, I nevertheless put the tests in their own project. Call it a habit, if you like ;-)
Now, on to testing the component. Here´s an outline of the class MyStack:
public partial class MyStack<T>{
private List<T> stackElements = new List<T>();
public void Push(T element)...
public T Pop()...
public int Count...
public bool IsEmpty...
}
Which method to test first? Well, I´d say Push() should be tested first, since all other methods´ functionality somewhat depend on Push() working correctly.
How could a test for Push() look like? Maybe something like this:
using NUnit.Framework;...
[Test]
public void TestPush()
{
MyStack<int> s = new MyStack<int>();
s.Push(42);
Assert.AreEqual(1, s.stackElements.Count, "Push failed");
Assert.AreEqual(42, s.stackElements[0], "Element not on stack after Push");
...
}
Please note: Since I just want to test the Push() method, I don´t want to rely on Count or Pop() to check if Push() worked correctly. If a test like
s.Push(42);
Assert.AreEqual(42, s.Pop());
failed, I would not know, whether Push() or Pop() was not correct. That´s why I want to check Push()´s success by looking at the private state - stackElements - of the MyStack object.
Since this state is not visible to any client of a MyStack object, be it within the same component or outside, I can´t put this test in my separate test project.
But there´s a solution to this dilemma: I put the test code in a static method of the class to be tested - and call it from my test project. However, I don´t want to clutter the code of the MyStack class with additional test methods, so I separate any test methods - probably it´s just a few of this kind, that need to be that close to the code under test - into another file by making MyStack a partial class:
MyStack.csprj/MyStack.cs:
public partial class MyStack<T>
{
private List<T> stackElements = new List<T>();
public void Push(T element)...
...
}
MyStack.csprj/tests.cs:
public partial class MyStack<T>
{
public static void TestPush()
{
MyStack<int> s = new MyStack<int>();
s.Push(42);
Assert.AreEqual(1, s.stackElements.Count, "Push failed");
Assert.AreEqual(42, s.stackElements[0], "Element not on stack after Push");
...
}
}
Now, you might be asking:
- Why not a regular Unit Test method instead of a static method? Because a Unit Testing framework like NUnit cannot instanciate a generic class like MyStack<T>. A static method, though, does not require a prior instanciation of the class, but creates an object itself.
- Why a test in the class to be tested? Because only this way the test can access private members of a class´ instance.
Although the intra-component test method is static and not a regular Unit Test, if uses the NUnit framework by calling Assert methods. So it acts as much like a Unit Test method as possible. In order to execute it, though, it needs to be called from a regular Unit Test method, which I locate in the test client project:
test.MyStack.csprj/tests.cs:
[Test]
public void TestPush()
{
MyStack<int>.TestPush();
}
That´s it! All tests are factored out into a separate test client - except just for a few which need to be very close to the class to be tested. Those live in static methods to which tregular test methods delegate their work.
There´s only one drawback so far: The MyStack component now contains a test method which any client could call. That´s not how production code should look like.
To get rid of this problem, I surround the static test method as well as the regular Unit Test method calling it with a
#if DEBUG
...
#endif
preprocessor statement.
My component test bed now looks like this:
And I´m very happy with its as clear as possible separation between implementation and tests. Using a partial class for the few necessary tests within the component under development makes them very easy to spot and maintain and exclude in release code.