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.

13 Comments

  • Interesting method and I like it. Just as a tip, rather than using a #IFDEF statement around code you don't want compiled, I use a [Conditional(&quot;DEBUG&quot;)] attribute on the method. I find it a little cleaner.

  • I totoally disagree with this...



    1) You're exposing your classes as partial classes which means that anyone can come along and extend them but without using OO. This is just a bad use of partial classes.



    2) If you're using VS to do your testing, utilize the PrivateObject class to access private members, etc. Otherwise, create a general &quot;UnitTesting&quot; assembly you can use in your test cases that use Reflection to get into the private members.

  • @Matt: 1) A partial class does not expose anything to the outside. It cannot be extended in another assembly. So not just &quot;anyone can come along and extend&quot; it.



    2) I&#180;m not using VSTS and wanted to show a way to structure tests involving access to private members which works for any Unit Testing framework.



    And I&#180;m not a friend of using reflection, because thereby you introduce dependencies on hidden implementation details into the tests lying outside the class to be tested.



    Code within the class to be tested like my static method in the partial class, though, &quot;is allowed&quot; to know about private details of the class.



    -Ralf

  • Wouldn't friend assemblies solve the unit testing problem much easier?

  • @James: No, the InternalsVisibleToAttribute does not help, since then the assembly of the class to be tested would need to know the test client assembly. This would be the wrong direction for any dependency/coupling.

  • Why are you testing the internals? If your class is passing 100% of the tests you wrote around its public operations, then it really doesn't matter to any user of your class whether the private implementation fails in some way.



    Also, whenever you are tempted to test private implementation, you should take that as a signal to refactor your code into more classes and assemble your solution using composition.

  • @MB: Of course the TestPush() method tests an implementation detail. And I think I made clear I don&#180;t like that very much - but I at the same time I think it&#180;s necessary. Testing of MyStack has to start somewhere, so I picked the Push() method. And a new test should add only one new method to test. Testing two untested methods is like changing two parameters in an experiment. Not a good idea. So I&#180;m testing just Push(). In order to check if it&#180;s working correctly I thus need to rely on implementation details.



    This approach of course should be kept to a minimum - as I demonstrated by using it for just one method.



    Deriving a test class from MyStack&lt;T&gt; does not work, since it&#180;s a generic class which a testing framework cannot instanciate.



    The source you listed shows how not to test a stack class and I agree with them. They criticize exposing implementation details in the public interface just for testing purposes. But that&#180;s not what I&#180;m doing. Right to the contrary: I&#180;m showing an approach to keep such details hidden and (!) be able to use them when needed.

  • @bonder: I&#180;m using knowledge of implementation details in one of four tests because not using it would complicate the test by using two untested methods where I just want to test one.



    I agree with you this is not the best way to test. Testing just the public interface of a class is the way to go - whereever possible. But as I tried to show, it&#180;s not possible here.



    I don&#180;t agree with you that any perceived need to test with the help of implementation details is a sign for refactoring - as my example shows. Refactoring would not help in testing the MyStack&lt;T&gt; class in any way.

  • I prefer having my test projects share the same namespace as my production code. Instead of &quot;test.MyStack&quot; I'd have &quot;MyStack.Tests&quot;. An added benefit is that you get an automatic &quot;using&quot; statement.

  • @Udi: I see what you mean. Yes, why not... But since I like my tests to be &quot;real clients&quot; of the code under test, I want them that close to it. A production client of the component to test likely is not in the same namespace, so I like to see what&#180;s necessary to work with the component (e.g. a namespace, an interface).

  • @Alois: You have a point here: My test code within the assembly under test would not be included in a release build. If that&#180;s to be tested, you can&#180;t use my suggestion - or you remove the #if DEBUG pragma.



    Nevertheless it doesn&#180;t seem to feel right, including the name of a test project into the assembly under test. This would force any test code to go into that test assembly.



    Also, publishing private stuff as internal does not feel right either.



    -Ralf

  • I think we should differentiate between to test scenarios:
    1. Testing of the components (public) contract.
    2. Testing during development, while the contract is not fulfilled yet.

    For 1) you need only access public stuff. Right!

    For 2) you need often to access private or internal stuff. You don't want to wait until your component is working, in order to do tests. You want to see if things work like you intended it. Did you close an open file, release an unused buffer? ...

    For these reasons I like Ralf’s idea of partial classes.

    I would however not necessarily put test code in the partial class, but implement public accessors there. Like making "private List stackElements" public through a public property like:

    public List TestAccess_stackElements{
    get { return stackElements; }
    }

    -Olivier

  • Unit testing a library with a nice public interface is one thing. Unit testing an application is quite another. Consider an active object with a TCP/IP interface and no public methods, except maybe Dispose(). You cannot unit test this without using reflection. I don't like reflection since it is string based name binding is brittle and complicates refactoring.

    Using the partial keyword provides a neat solution. This keyword only instructs the compiler to allow other source files to add to the class, and does not change the behaviour or interface of the class in any way. The final type is created at compile time and cannot be changed later.

    I also like to keep unit tests and implementation code separate, but I don't like the #if DEBUG approach. One solution is not to reference the assembly you are testing, but to include the class's source code file directly in the unit tests' assembly (add it as a link if necessary). In this way the implementation code stays completely untouched, and you can test any kind of build. Now it also does not matter how you change the class's interface during testing, as clients would have to reference the unit test assembly to see the modified class (you can also not reference the unit test and production assemblies at the same time).

Comments have been disabled for this content.