Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

TDD: Consistent test structure

While pairing with Damian we came across the fairly common situation where we’d written two different tests – one to handle the positive case and one the negative case.

While tidying up the tests after we’d got them passing we noticed that the test structure wasn’t exactly the same. The two tests looked a bit like this:

[Test]
public void ShouldSetSomethingIfWeHaveAFoo()
{
var aFoo = FooBuilder.Build.WithBar("bar").WithBaz("baz").AFoo();

// some random setup
// some stubs/expectations

var result = new Controller(...).Submit(aFoo);

Assert.That(result.HasFoo, Is.True);
}
[Test]
public void ShouldNotSetSomethingIfWeDoNotHaveAFoo()
{
// some random setup
// some stubs/expectations

var result = new Controller(...).Submit(null);

Assert.That(result.HasFoo, Is.False);
}

There isn’t a great deal of difference between these two bits of code but the structure of the test isn’t the same because I inlined the ‘aFoo’ variable in the second test.

Damian pointed out that if we were just glancing at the tests in the future it would be much easier for us if the structure was exactly the same. This would mean that we would immediately be able to identify what the test was supposed to be doing and why.

In this contrived example we would just need to pull out the ‘null’ into a descriptive variable:

[Test]
public void ShouldNotSetSomethingIfWeDoNotHaveAFoo()
{
var noFoo = null;

// some random setup
// some stubs/expectations

var result = new Controller(...).Submit(noFoo);

Assert.That(result.HasFoo, Is.False);
}

Although this is a simple example I’ve been trying to follow this guideline wherever possible and my tests now tend to have the following structure:

[Test]
public void ShouldShowTheStructureOfMarksTests()
{
// The test data that's important for the test

// Less important test data

// Expectation/Stub setup

// Call to object under test

// Assertions
}

As a neat side effect I’ve also noticed that it seems to be easier to spot duplication that we can possibly extract with this approach as well.

This entry was posted in tdd. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

6 Responses to TDD: Consistent test structure

  1. Hi Mark,

    while I generally agree with your points (it’s indeed a very good thing to have similar structures among all tests, this makes life of later readers MUCH easier), I think one should be quite cautious with that:

    “to spot duplication that we can possibly extract with this approach as well”

    Most of the times, it is NOT recommendable to refactor code duplication in tests. Rather it’s better to have everything inside a single test method to maintain readability and understandability of the tests. – As long as it is not too much and too complicated, of course; but that’s almost never the case for TDD-style unit tests.
    Refactoring of test code is mostly a bad idea, especially when it comes to unit tests stemming from a TDD process. You shouldn’t have to browse for any other methods to understand what a certain test is doing…

    This (readability issues) is the reason why I mostly try to avoid helper methods and the like in unit test fixtures and rather accept massive code duplication instead in my TDD unit tests. I made very good practical experiences with that method and strongly recommend this as a general way of writing tests to TDD-beginners…

    Thomas

  2. Paco says:

    I don’t understand the number of mouse clicks is increased when you have separate methods. Can you explain that?

    I think it’s much easier to see what failed when there are separate test for each assertion. The assertion method names tell exactly what failed. When you click on a test in visual test runner, the failing code will be shown to you immediately. To do this, you need some infrastructure code that executes the Arrange and Act method before executing each Assert method, like

    public class TestBase
    {

    [AttributeToCallThisMethodBeforeEachTest]
    public void SetUp()
    {
    Arrange();
    Act();
    }

    public virtual void Arrange() { }

    public virtual void Act() { }
    }

    I would use
    var result = new Controller(…).Submit(noFoo);
    as implementation of the act part, because two methods are executed, the creation of the controller (arrange) and the submit method (act).

    I never need to setup expected values, because it’s less code to do not, and it’s part of the assert part, not of the arrange part.

  3. markhneedham says:

    @Paco – I’ve tended to shy away from dragging assertions out into separate methods because it seems to increase the number of mouse clicks/ctrl-b’s that you need to work out what something failed if it did.

    @Michael – in this case actually it was identical but I would favour the duplication in this case because I think it’s better to keep as much of the information about the test in the test body rather than looking to remove duplication and making it more difficult to understand what the test is doing at a glance.

  4. Michael says:

    It is not often that I feel the need to comment on a CodeBetter blog post but… This comment may not be on target given your exact case since we don’t know if the some random setup and some stubs/expectations are truely the same between your snippets of code. If they are, this would be a code smell where you should consider refactoring. Using either the TestCaseAttribute in NUnit (which may not work in this case due to the object creation) or a driver method that creats the objects and executes the test with them would remove this duplication.

    Thanks, great post.

    Michael

  5. Paco says:

    I recommand using separate methods for the separate things:

    public class SomeTestClass
    {
    void Arrange() { }
    void Act() { }
    void AssertOneThing() { }
    void AssertSomeOtherThing() { }
    }

    This makes the code easier to change and read, because you just have to change one method to make one change, and you don’t need to explain the arrange, act and assert in one method name.

  6. zhaorui says:

    I agree with Damian

    I also recommend this:

    public void TestMethodStructure()
    {
    // Assign

    // Act

    // Assert
    }

Leave a Reply