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.
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].
@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?
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.
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.