Let’s say that you have a function that takes two strings and returns a single string. Being a responsible 21st-century developer, you want to create a set of unit tests.
You certainly want to test the boundary conditions, when nulls are passed in. And certainly what if the strings passed in are empty? Oh, and what if they contain escapes? And what about their encoding? Oh, and what about…
Even the simplest function generally has a large number of corner cases that ought to be validated. True, managed code makes nulls and over- and under-flows and (heaven forbid) boundary violations less catastrophic, but, caught as a runtime exception or not, an unanticipated value causing an abnormal end to your program is a problem.
Most handwritten test suites skip over the boilerplate cases, because the test developer jumps ahead to “realistic” or “interesting” cases rather than spending their valuable time validating the almost-certain-to-pass simple cases. Of course, battling such “of course this part works” assumptions is key to unit-testing.
One tool to help get through the boilerplate corner cases quickly is FIT (Framework for Integrated Test), which reduces inputs and outputs to expected values in a table. Even more sophisticated, but requiring a larger commitment, are tools that support FIT-style tables in the context of Behavior-Driven Development. Cucumber is very popular in the Ruby world, and there are a number of .NET frameworks supporting the “Gherkin” language, such as SpecFlow.
Pex, a Microsoft PowerTool for Visual Studio 2010, is the first tool for white-box testing that I find compelling. It is not a brand-new tool—I discussed it briefly two years ago—and the PowerTool is nicely polished. I feel that the crucial benefit in Pex is that it works by analyzing the internal structure of the code under test.
Rather than exhaustively running every combination of possible inputs (impractical for non-trivial functions) or just using boiler-plate “interesting” values for the input parameters (min and max values, -1, 0, 1, etc.), it uses constraint programming to seek values that exercise all possible code paths in the target function.
The results can be deceptively simple-looking. Handwritten tests almost always have human-significant values: “InvalidEmail” and “Robert’);DROP TABLE Students;” and so forth. The output of running a Pex “exploration” has less obvious data, a FIT-style table with lots of single-character and “ ” strings.
One important lesson of the past decade is that testing cannot be palmed off to non-developers. A corollary is the suspicion that tools that automatically generate test cases are likely to be deceptively reassuring. We know that less-experienced and less-competent programmers will sometimes blindly follow compiler warnings and errors and generate intricately wrong code “to satisfy the compiler.” It seems reasonable to fear that the test results generated by a tool in an early run might be accepted by a lazy developer and become enshrined in the test suite.
Two ways that Pex battles this is by making it very easy to e-mail the responsible developer and by generating argument-validating preconditions to your code. Nonetheless, you can’t abstract- away incompetence, and I doubt that Pex would duplicate the “Little Bobby Tables” SQL injection attack shown previously.
The Pex PowerTool is now paired with Moles, another Microsoft ResearchMSR project thatwhich can “detour” functions so that they are handled by (hopefully) fast-performing and consistent stub functions. So a call to, say, a Web sService providing a stock quote, can be replaced with local, hard-coded values. (If Moles has the same capabilities as Microsoft Research’s C-based “Detours” library for Win32, it can be used for all sorts of wild metaprogramming.)
Finally, one can use Pex with yet another Microsoft ResearchMSR project called “Contracts.” Contracts supports “Programming By Contract,” as popularized in the Eiffel programming language. Contracts identifies the guaranteed preconditions, postconditions, and invariants of a method call; for instance, you might say that the argument to a square root calculation must always be non-negative. If you check for that in your code and throw an ArgumentOutOfRange exception, that’s a parameter validation. If you specify it as a precondition, your code is considered correct no matter how it handles negative numbers (the contract was broken by the calling code that ignores the contract’s preconditions).
Programming by Contract provides more guidance than unit-testing for both creating tests and understanding design intent. Pex incorporates the Contracts specifications and creates more highly- tuned tests.
Although I think we should be careful about relying too much on automating tests, Pex, Moles and Contracts properly keep the focus close to the code. I recommend all .NET developers become familiar with these tools and start using them in production projects.
Larry O’Brien is a technology consultant, analyst and writer. Read his blog at www.knowing.net.