Writing unit tests

Yesterday I was bored.  When you take this bloke out of the Igloo and it’s to warm to his tongue stuck to a parking meter he tends to write manifestos.  This one stemmed from having to fix a test fixture that failed when the tests were run in random sequence.  The result was an email outlining what I think are the primary concerns each developer should have when they write their unit tests.  These were my thoughts (expanded a bit because sometimes I care to share more with you than my coworkers).

  1. Never create module level object variables in your test fixtures so that they can be reused in many tests.  Often this will cause false positives and negatives simply due to test execution sequence.  Creating new objects in every test is preferable as it allows you to know that the starting state of those objects is clean and pure.  There can be exceptions to this rule such as creating one MockRepository (RhinoMocks) that is used to create mock objects across all tests.  If you’re going to break this rule you’d better be able to justify it to me without breaking any of the other rules listed here.
  2. Treat tests as a specification of what the code under test should be doing.  If you read a test without reading the code, you should be able to say, with confidence, how the code under test is expected to perform.  I’m a firm believer that refactoring tests decreases your ability to adhere to this principle.
  3. Don’t refactor test code to the same extent that you refactor application code.  Test code is utilitarian by nature and also requires high levels of readability (see point #2) and maintainability.  Because of this test code should not be exceptionally, or even remotely, elegant.
  4. If you find that you’re struggling to write tests that cover all expected behaviours of the code, or to get a test to pass, then the code under test is probably too complicated.  Complex tests are an indication of complex code, which in turn should be an immediate sign that the code under test needs to be refactored to adhere to Single Responsibility Principle.  When I do code reviews, complex tests fully qualify as a code smell.
  5. Never write tests that rely on other tests to be executed either before or after that test.  A unit test should be an encapsulated piece of work that is ignorant of all external functionality and sequencing with the exception of the method that it is testing and the Setup and Teardown methods.
  6. Avoid mixing process (RhinoMocks.Expect.Call(…)) and state (NUnit.Assert.AreEqual(…)) based tests in the same test method.  This relates back to readability and documenting the expectations of the code under test(see point #2).  If you write one test for process based testing, I know exactly how to expect the code under test to behave with regards to it’s dependencies.  If you write another test for state based testing, I know exactly how to expect the test to work with regards to its data.  Mix the two together and I have a muddled concoction of data and dependency information presented to me and it takes more effort to separate the two and clearly understand each.

Of these 6 points, number 6 has proven to be the most controversial at work.  I will readily admit that it is more rooted in personal preference than the other 5, but I think that I’m backing up my personal preference with some decent reasoning.  If anyone has anything to add (more points, better/different explanations) I’m all ears.