Roy Osherove

View Original

Basic Guidelines for using RowTest and data driven tests

I took a look at the code that was used to create the NUnit RowTest Extension (which is pretty neat) and found this in one of the samples:

image

The highlighted areas in the code show something which I feel is problematic and is the manifestation of a unit test anti-pattern which the XUnit book calls "Interacting tests". basically, there is a dependency that is created by the first and second row, in which the second row cannot run individually.

Bad Naming once again leads to confusion

I'm not saying that this test is bad, because perhaps that is what they author was trying to test - that the class level members are not destroyed between each row. I guess we'll never know because the naming of the test is problematic - it doesn't say what the expected behavior is, or what the scenario\context is. just what the general object being test is (and I'm not even sure about that). Still, it makes for a nice example for your own tests.

The symptoms of interacting row tests

Back to the issue at hand, the symptoms detailed below are caused by the wrong usage (in my mind) of the RowTest facility. The second argument in the RowTest is provided for the sole purpose of changing the behavior of the class under test (changing s_classMember) so that the next row will be able to assert on the string.

Now this prevents you from doing the following without going into annoying routines of code replacements:

  • You can't remove a row test without making sure you don't break any other rows
  • You can't just insert a row test randomly in between without possible breaking another row
  • You have to read the code in the test to understand what the row test is doing.

Introducing dependencies between row test essentially makes the specific test method unmaintainable to a large degree. Since row-tests and data driven tests are a fairly new syntax for most of us, here are a few guidelines I try to follow:

Some Basic Guidelines

  • Treat each test row as if it will be the only one on the test method (so that you don't feel the need to interact with other rows).
  • Test rows must be a simple as possible to understand - that is - have no logic. In this case we do have logic int he test row (pertaining to the next row)