Thursday, January 26, 2012

Structuring Unit Tests

Phil Haack wrote an article entitled "Structuring Unit Tests" (I highly recommend reading it) and it started us talking about how we could adopt this method of test structuring in our projects. The basic structure is a test class that contains test classes for each system under test.

We are a test-first shop and use MSTest as our testing tool of choice. So, step 1, write some tests that follow the structure (I am just going to reproduce Phil’s example using MSTest):

[TestClass]
public class TitleizerTests
{
[TestClass]
public class TheTitleizerMethod
{
[TestMethod]
public void ReturnsDefaultTitleForNullName()
{
//test code
}

[TestMethod]
public void AppendsTitleToName()
{
//test code
}
}

[TestClass]
public class TheKnightifyMethod
{
[TestMethod]
public void ReturnsDefaultTitleForNullName()
{
//test code
}

[TestMethod]
public void AppendsSirToMaleNames()
{
//test code
}

[TestMethod]
public void AppendsDameToFemaleNames()
{
//test code
}
}
}

Right away we noticed two problems. When you use the keyboard shortcut Ctrl+R, C all tests in the class are run. This works great using this structure because it runs all tests in the SUT class allowing you to isolate the scope of tests to run. The problem is when you want to run all tests in the top level class. If you try Ctrl+R, C at the top level, all tests in the namespace get run. This was easily solved by creating a unique namespace for the test class to reside in:

namespace TestStructure.UnitTests.TitleizerTestContainer
{
[TestClass]
public class TitleizerTests
{
[TestClass]
public class TheTitleizerMethod
{
[TestMethod]
public void ReturnsDefaultTitleForNullName()
{
//test code
}

Now we can run all tests in the class without issue.

The second problem is code duplication. If some setup is required for my class before I can perform my tests, I have to repeat it for each class because nested classes do not have access to their parent members. Our solution to this is to make all of the children inherit from the parent test class like this:

[TestClass]
public class TitleizerTests
{
protected Titleizer target;

[TestInitialize]
public void Init()
{
target = new Titleizer();
}

[TestClass]
public class TheTitleizerMethod : TitleizerTests
{
[TestMethod]
public void ReturnsDefaultTitleForNullName()
{
//act
string result = target.Titleize(null);

//assert
Assert.AreEqual(result, "Your name is now Phil the Foolish");
}

[TestMethod]
public void AppendsTitleToName()
{
//act
string result = target.Titleize("Brian");

//assert
Assert.AreEqual(result, "Brian the awesome hearted");
}
}

This is obviously a contrived case where no actual setup is required, but now the TestInitialize method will run before each test in my child classes.

Plus the test are much easier to read:

image

image

We are just stating to experiment with this test structure, but so far it looks very promising.

13 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. good article. i like the grouping and readability. you may want to change Appends... to Prepends... for better clarity of what the methods are actually doing.

    ReplyDelete
  3. I was trying this approach. It's fine as long as you're testing a concrete class. But what if you are making tests for an interface? I'd make an abstract test class that tests interface methods. Then I'd inherit this class to test a concrete implementation. With this approach, the "sub-classes for the methods" wouldn't be inherited. Any suggestions on how to address this?

    ReplyDelete
    Replies
    1. Interfaces really are not appropriate for unit tests as they have no behavior. If you have an abstract base class that implements the interface, then each concrete class that extends the base class should have their own set of unit tests. If the abstract base class has shared behavior or its own implementation of a method, this can be effectively tested using the very simple and excellent NSubstitute library to create a testable instance of your abstract class.

      Delete
    2. That's not what I meant. I want to write a set of tests against the interface methods, and reuse those tests for all implementations of that interface.

      It makes sense to me: My tests will enforce every implementation of that interface to comply with those requirements. Testing the interface signatures you're only checking if the return value of the methods is correct given a set of parameters. I see nothing wrong with it.

      Still I can't figure out how to use your approach in this scenario. I guess I'll be forced to place all the tests together in the abstract class.

      Delete
    3. I assume you are trying to reduce the duplication.

      One way to accomplish your scenario might be to create a series of "helper" methods that accept a parameter of the interface you are testing in the parent test class. Then call the methods in the nested test class for each type.

      Delete
  4. Neat idea. Visual Studio does not like the fact that I have initialized my protected variable from the outer class in the TestInitialize of the outer class. Resharper suggests converting the field to static but I am not like that suggestion - not sure if having a static field initialized in the TestInitialize and used by all the other tests is a good idea. Am I wrong in thinking this?

    ReplyDelete
    Replies
    1. Nevermind - this was a typo on my end.

      Delete
    2. That's what I was going to suggest. I've gotten burned on that a few times too. Thanks for reading the blog, I hope you found it useful.

      Delete
    3. I see that the tests in my outer class get run multiple times ( 1 + number of inner classes). Is this right? Any way I can avoid it? I am using MSTest and Test Explorer.

      Delete
    4. In Nunit and Re-sharper I worked around this by using the outer-class (TitleizerTests) purely as a container but add a separate abstract class outside of it but in the same namespace in which I place [TestInitialize], protected fields, helper methods, etc.., i.e: "TitleizerTestsBase".

      Then any inner test class inherits from "TitleizerTestsBase" instead, leaving the parent class act purely as a container.

      As the outer parent Class then only acts as a container you might even move "TitleizerTests" in to the namespace then, i.e: "namespace TestStructure.UnitTests.TitleizerTests"

      All you have then in the namespace is the abstract base class and all the test classes inheriting from the abstract base.

      This works for me and both, Re-sharper test runner NUnit app. Not sure this will also work for MSTest but you could give it a try and see.

      Delete
  5. Thank you!
    This was just what I was looking for.

    ReplyDelete
  6. With xUnit you add the initialization code to the TitleizerTests constructor and it handles it ok. I'm not sure if it's good to do initialization in constructor tho...

    ReplyDelete