Strategies for Structuring Unit Tests

Some time ago I read a good article on some common TDD anti-patterns by James Carr (which lives right here.) Great, yeah, those are some anti-patterns. So what about patterns?

I wouldn’t say what follows are necessarily TDD patterns; they’re simple techniques for structuring your unit tests. TDD is a great design methodology and it’s absolutely essential to have a robust test portfolio as a refactoring safety net. That said, you are incurring more care and feeding debt simply because you now have more code to maintain.

A well chosen strategy for testing unit tests can ease this burden…

Test Method per Feature

The word feature could easily be substituted for requirement, use case, or user story. This strategy is useful for keeping features granular. As you generally want to keep your unit tests fairly small, this will force you to keep your features fairly small. Agile practices advocate keeping your units of work fairly tiny, e.g. limiting the size of your backlog items or users stories. Having a test method per feature is one way keeping size of test, feature, and business requirement in lockstep.

Test Methods as Examples

Peter Provost and Brad Wilson mentioned in a talk I attended that they had taken to calling TDD example driven review (if I am remembering correctly). The idea is that your tests become specifications of how to use the classes of your app in consumer code. A nice advantage of this approach is that tests become great starting points for developers new to a particular codebase, a kind of developer documentation. You’re feeding two birds with one seed, and that’s always a wonderful thing.

This tactic makes a lot of sense when applying the TDD practice; remember it is fundamentally a design methodology that produces tests as a positive side-effect that aid in refactoring (feeding back into the design). In some regards I wish TDD had called Design By Test, DBT… but that’s another post.

This strategy works well with the test method per feature strategy above. You’d start your test fixture with a comment block that enumerates the list of features encapsulated by the unit under test. As you go through the list, follow the usual red-green-refactor process of creating test methods, writing the implementation, and tweaking the design to satisfaction. As you go, you remove list items from the comment block.

I like this strategy very much. When doing tests as examples I like to prefix my test class with Example.

Test Fixture per Feature

In this scenario you might have a fairly beefy feature that can benefit from multiple tests or you have a feature that is distributed across several units (classes). Generally your test fixtures (classes) will have a one-to-one mapping to a corresponding class (the class under test), but it is occasionally beneficial from an organization and maintenance perspective to break out more complex features into their own fixture.

Test Fixture per Asset

In a domain model adhering to DDD patterns you’d have a test for each Aggregate, Service, Repository, and stand-alone Factory. We use this strategy at Xclaim for our domain model layer. This brings up a larger issue of asset-based development that’s well beyond the scope of this post. I will briefly mention that I like to conceive of the systems I build as “Lego Kits”, and look to conceptually differentiate the red bricks from the green bricks from the mini-figs.

Test Library per Layer

Model your test libraries by architectural layer. This could mean a single test library for multiple assemblies: as long as those assemblies serve the same architectural layer (e.g. data access layer, presentation layer).

Test Method per Defect

In this strategy, you would create a test method when an end user or the test team reports a defect. Typically this will happen after you’ve debugged and diagnosed the defect in question. The idea here is to ensure that the defect doesn’t rear its ugly head again and that you’ve given yourself some “refactoring insurance” as you evolve the codebase.

Inline Tests

Write your tests in the same project as your production code. Yes, this is one way to go, but I don’t like this strategy; it’s too mixed up and hard to manage. If you were to go this way you’d probably want to strip test classes from your automated builds (at least when building in release mode). This requires hand tuning (e.g. you couldn’t ms-build or nant build right from a project/solution file. Even though I’m not a fan, I thought I’d include this strategy as someone out there might be using it and have some insight on benefits/techniques.

This is by no means an exhaustive list, but I thought I’d try and start a conversation:

  • What tips do you have for structuring your test libraries?
  • Are stale unit tests an issue in your shop?

Comments (6) left to “Strategies for Structuring Unit Tests”

  1. DotNetKicks.com wrote:

    Strategies for Structuring Unit Tests…

    You’ve been kicked (a good thing) - Trackback from DotNetKicks.com…

  2. James Carr wrote:

    Good list. Another one I would add is “one assertion per test method.”

    One thing that always bites me is seeing a a test where the first test method brings back an object and does 20 random assertions against it, and then it is copy and pasted for each test method, with the intent hidden somewhere along those 20 assertions.

  3. Dave wrote:

    That’s a solid tip. At very least “cohesive” assertions per test method, which should limit assertions (generally) to 1-5…

  4. Jeremy Brayton wrote:

    Thank you. I have a hard time coming up with adequate starting points for tests. It’s not that I’m an idiot, it’s just that I can change my mind about something 500 million times before I think it’s the right way(tm). I realize a lot of it is personal preference, but having it broken down severely helps. Having something like this to slap me into somewhat proper alignment is definitely a good thing.

  5. Judah wrote:

    I agree with James, 1 assertion per test. I’ve found tests to be smaller this way as well as more readable.

    We have some 10 projects in a solution. I used to have all unit tests in a single project, but things started getting hairy. I’ve since broken the unit tests up into projects. Now, 10 projects means 10 unit test projects. Easier to find and maintain and allows you to run only the tests your interested in fairly easily.

    Use TestDriven.NET (Visual Studio integration). This makes running your tests a breeze: just right-click and Run Tests from the context menu. Or better yet, assign a hotkey to run tests. I’ve assigned CTRL+\ to run the current test fixture set, and CTRL+] to run the last test that was run.

    Name your test fixtures something other than the class or object they’re testing. For example, Foo.cs and FooFixture.cs.

    Don’t skimp on tests if you’re in a hurry, it’ll come back to bite you.

    Lastly, run tests regularly and always before publishing latest bits.

  6. Dave wrote:

    Jeremy,

    I hear you loud and clear. Most of this stuff I discovered (and am discovering) through trial-and-error. I lucked out and got to thinking about this stuff when I saw that presentation by Brad Wilson & Peter Provost (awesome presenters, btw).

    Judah,

    Great tip. I’ll incorporate this into a future revision of this post. I agree with you 100%. Right now I’m putting the tests into their own “Tests” solution folder. One thing I’d like to try is putting them side-by-side with the library under test, e.g.:

    XS.Model.Contacts
    XS.Model.Contacts.Tests

    One thing that can be annoying is losing your place w/ Solution Explorer, so I’d be looking to keep everything w/in one scroll through this tactic.

Post a Comment

*Required
*Required (Never published)