the Compartmented Robust Posix C++ Unit Test system |
|
---|
1.1. | Why was crpcut written? | |||
Because, even though there are many C++ unit test systems out there, none covered all the desired properties, although many covered some:
| ||||
1.2. | Why run tests individually in separate processes? | |||
Because it protects the tests from each other. One failed test cannot easily corrupt the environment for any other test. It also makes it possible to handle inadvertent crashes and even infinite loops as any other failure. | ||||
1.3. | But doesn't forking all these short lived processes consume a lot of time, making crpcut very slow? | |||
Not really. The overhead is very low, typically much less than a millisecond, so you would need a very long suite of very short tests for this to matter. Experience shows that build-time for a test program is typically much longer than the run-time, and the test programs are usually build-once/run-once. In addition, crpcut allows you to run tests in parallel, which on a multi-core CPU can reduce run time considerably, especially if individual tests are time consuming. | ||||
1.4. | Why not throw exception to report errors? After all, that is the natural C++ error reporting mechanism. | |||
It is a bad idea just because it is the natural C++ error reporting mechanism. A unit under test could accidentally prevent an error report from being captured. Consider the following class, that needs testing: class catchall { public: catchall(int n); }; inline catchall::catchall(int n) { try { stubbed_function(n); } catch (...) { std::cerr << "something bad happened, but I saved the day" << std::endl; } } Assume a macro int expected_value = 0; void stubbed_function(int n) { THROW_ON_ERROR(n != expected_value); } Now we write a test for this program: TEST(construct_catchall_with_wrong_value) { expected_value = 3; catchall object(2); } See what's wrong? | ||||
1.5. | Why terminate a test process on the first found error? Isn't it better to continue and see if there are more errors? | |||
What is better depends on the situation. Some times it is no use to continue a test after a failed condition, because the state is not one that makes sense for the rest of the test. At other times, more checked conditions can give more insight into what went wrong. This is why crpcut offers both ASSERT macros, that terminate the test process on failure, and VERIFY macros, that allow execution to continue even when the test is marked as failed. Which is better to use is a judgement call from case to case. | ||||
1.6. | Why the macros and template trickery, instead of a more normal C++ use? | |||
It was not an easy decision, but the reason is simple. As ugly as the implementation is, the ease of use is phenomenal. You focus on writing test logic instead of writing boiler plate code to match the test engine's implementation. | ||||
1.7. | Why require functionality that isn't even standardized yet, like variadic macros and decltype? | |||
Correction, they are standardized in ISO/IEC 14882:2011. While few, if any, compilers supports the standard in full, most compilers today supports the functionality required for crpcut, and has done so for years. The advantage gained in ease of writing test cases far outweighs the disadvantage of not working with a select few old compilers.
|