Code-Reuse in unit tests is more important than you think - ISerializable - Roy Osherove's Blog

Code-Reuse in unit tests is more important than you think

Scott Bellware mentions how he really likes the new ideas in XUnit.NET, especially the lack of [Setup] and [TearDown] methods. (Brad Wilson wrote in the comments to my post about XUnit.NET that you can mimic this behavior by using the constructor of the test class and the IDisposable.Dispose() method of the class -just implement IDisposable) .

He especially says this:

"I think that Jim is on the right track, but I'm the kind of guy that feels that a test class's greatest responsibility is to document behavior in the clearest possible way, even if that means sacrificing design qualities best reserved for functional code - like reuse."

(emphasis mine)

That's silly. Scott tries to write "pure" TDD tests by saying that tests are 100% different than functional code, but they are not. Proof -- Tests that don't have code re-use in the form of setup and teardown or various specialized factory methods will be harder to maintain because every time you change some semantic aspect of your production code (constructors, initialization phases etc..) many of your tests will break and you'll have to fix them all one by one because you don't have re-use for object creation in your tests. Pretty soon you'll just stop maintaining your tests and that means you'll stop writing new ones. Ta-da! mission accomplished.

If we don't all realize that in the real world tests have to be as maintainable (or even more so) as production code, we won't come out of the middle ages of unit testing.

he then goes on to say some rules he has about Setup methods. one of which is:

"Don't put anything in a setup method that doesn't pertain to every test method in the test class"

I completely agree with this. You NEED to have re-use but you also need to make sure you don't abuse these abilities.

Published Saturday, September 22, 2007 6:51 AM by RoyOsherove

Comments

Saturday, September 22, 2007 1:08 PM by Jeremy D. Miller

# re: Code-Reuse in unit tests is more important than you think

Marking the test fixture as IDisposable isn't enough.  I don't know about you, but the only time I need to use TearDown is to take down static data that could bleed into other tests, not as a place to dispose of resources.

I've often found that sometimes reusing some methods and code inside of unit test fixture classes can make the unit tests more declarative and easier to understand.  

"Don't put anything in a setup method that doesn't pertain to every test method in the test class"

I think the biggest thing I'm going to change in my TDD style from now on is ditching the one testfixture class per production class way of organizing unit tests.  That does lead to some ugliness.

"If we don't all realize that in the real world tests have to be as maintainable (or even more so) as production code, we won't come out of the middle ages of unit testing."

I absolutely couldn't agree more.

Saturday, September 22, 2007 2:06 PM by Brad Wilson

# re: Code-Reuse in unit tests is more important than you think

Jeremy, I agree with what I believe is one your points: NUnit made a mistake by not creating a new instance of the class for each test. With xUnit.net, you won't need to clean up shared static state between tests, because your constructor (SetUp) should be creating non-static state which will automatically be cleared between runs by virtue of getting a new instance per test method.

Most of the cases where I've wanted TearDown -- rolling back a database being the most common -- are things I would rather implement as cross-cutting concern attributes like [AutoRollback].

Saturday, September 22, 2007 5:38 PM by Rasmus

# re: Code-Reuse in unit tests is more important than you think

I completely agree with you. Unit tests have to be at least as good as the code it tests.

Not only does it make sure, that the code is doing, what we expect. But it is also conveying the intentions of the code to the other developers, who are to maintain the system the code is part of.

If the tests are badly designed, we not only loose maintainability and thereby confidence in the code. We also loose an important part of the documentation of the system.

Sunday, September 23, 2007 8:32 AM by Jeremy D. Miller

# re: Code-Reuse in unit tests is more important than you think

@Brad,

I'm not talking about state in the TestFixture classes themselves.  I'm thinking specifically of static cache's or IoC container state that could spill out between unit tests.  Just creating a new TestFixture class on every unit test doesn't necessarily help in that regard.

Another example that I hit a couple months back is screen testing.  I use a combination of NUnitForms and Fit to drive tests directly through a WinForms screen.  What I quickly found out is that if I left a screen open at the end of one test without closing it down the  next UI test in a completely different TestFixture would fail.  Using a TearDown was a cheap way to clear up this problem.

Granted, you don't want to put yourself in that position, but it still happens, especially in integration tests.

I'm not sure that I'd call a new test fixture instance per unit test a real improvement.  How much does that slow down the testing?

Sunday, September 23, 2007 12:29 PM by Joe Chung

# re: Code-Reuse in unit tests is more important than you think

Quis custodiet ipsos custodes?  ("Who watches the watchmen?")

Tuesday, September 25, 2007 5:15 PM by Wendy Friedlander

# re: Code-Reuse in unit tests is more important than you think

I totally agree with you Roy. Making changes to tests is a lot easier when they are written with good coding practices. I've see hours of refactoring that could have been avoided if better practices were followed.

If you find your setup code is too big or that you're lost scrolling through your test, its a smell your class does too much.

Monday, October 01, 2007 7:49 PM by Anthony Bailey

# re: Code-Reuse in unit tests is more important than you think

If you really are testing an isolated, independent unit then I venture that a test which is complicated enough to require code re-use can be considered a code smell - the production code class should be easy enough to use that you don't need lots of complicated set up. And you can often avoid complicated logic in the assertions by using a collection of specific "this input leads to this output" tests, which I find to be a valuable  (because conceptually perpendicular) cross-check of the more procedurally defined behavior in the production code.

Like any code smell, it's relative. I'm quite happy with using helper methods for abbreviation purposes, for example. But keep the conditionals out of the test machinery, please.

I appreciate that xUnit frameworks are used for developer (rather than acceptance) level testing of integrated systems as well as more primitive units, and in those instances my nose is much less sensitive.

Friday, October 05, 2007 9:40 AM by DavidDylan

# re: Code-Reuse in unit tests is more important than you think

I do think there are legitimate differences in the intent of production code and test code.

It's good for production code to be self-documenting, but this is one of many design factors that has to be taken into account: it has to work in the context of the existing code, it has performance goals to meet and so on.

It's much more important for test code to be self-documenting.  Why?  For a start, because it isn't subject to the same level of QA.  Peer reviews might include unit tests, but do reviewers really apply the same level of scrutiny to unit test code as they do to production code?  And how many people write unit tests for their unit tests ...

So I agree that some design qualities of functional code don't apply (I wouldn't say 'sacrificed') to test code.

But reuse and maintainability don't fall into that category.  It's vital that tests are maintainable.  If chunks of code are duplicated all over the place, this is less maintainable.  It's also less readable.  Repeating a complex set-up routine in test after test makes it harder to identify the main focus of the test - the bit that actually does the testing.

But maybe 'SetUp' and 'TearDown' aren't the best place to factor this code into.  They enforce a particular rule for organising tests - tests are organised into fixtures according to their set-up requirements - which reduces our ability to organise them in the way that makes most sense to us.

SetUp and TearDown are also too near the edge of the radar - I don't call them when I write my test, so I can too easily forget about them.  I prefer a style where the test describes, completely but succinctly, what it is doing.  In other words, it explicitly does the set-up itself.  The details of this setup can be delegated to some helper so that the structure of the code in a test case is

// few lines of setup

// detailed test actions

// assert expected outcomes

The helper can be reused by as many or as few tests as needed, in whichever fixture it makes most sense to put them in.