Parameterized Tests and RowTests
Mel Grubb talks about parameterized tests and why use it at all. He also raises the question whether having the ability to run parameterized tests can rule out usage of a framework. Personally, I think there are better ways to choose unit tests framework, but that’s not what this post about.
First, what are parameterized tests?
Regular tests, in NUnit, MSTest or other frameworks, are void methods that take no arguments. Parameterized tests are tests that certain frameworks (like Gallio) can run several times, each with a different set of input parameters and expected result. MbUnit uses the attribute RowTest, because each row contained a set of input parameters and a result, like a list. (And if you’re familiar with Fit/Fitnesse, the table metaphor may remind you of this as well).
With different parameters, I can run the same code, and not need to copy/paste it. This looks like a great productivity boost. But like Mel said, you’re losing some readability of the tests. If you came back to the code 2 weeks, months or years and look at the code, you wouldn’t know what differentiates the 2nd from the 3rd row. They all look the same.
So what Mel suggests is doing some refactoring, and increasing the readability by naming the tests accordingly, which I agree whole heartedly with.
But I’ll go a step further. Which test would you like to debug, if it broke? The one with the descriptive name that tests a single scenario, or the one with the laundry list of arguments? I’d go with the first.
One more thing: Now you can use the parameter in the test itself. Instead of arg1, you can now send to the tested method arg1*2. This starts to smell, you’re actually putting logic into the test. So, especially for beginners, or other people who would like to cut corners, I wouldn’t recommend using it.
Let’s say I have a component that does authentication. You give it a name and password, and it gives you back a yes/no result. For all the specific criteria I’d write specific and named tests: name is null, password is longer than 6 characters, password contains alphanumeric characters. Basically, if I can characterize it in words, I’ll write a unit test for that.
Now if I have a batch of hundreds of collected names and passwords, that I just want to make sure that pass authentication, I might want to write a parameterized tests, or just loop through all the list and assert the result. That’s in addition to the other tests I’ve already written.
By the way, if one of those breaks, I’d write a specific test for that as well. I’d recognize the case of failure, and characterize it. So the next time I come across, a case like that, I can debug more quickly.
Parameterized tests are nice. But do yourself a favor, for unit tests stick to the basics.