The last month, I've come across this issue several time, to my surprise (because it hasn't come up in the years before).
If you are wrapping tests around legacy code and the legacy code has a C-style assert in the production code, then what do you do? Do you write a test for it? If so, how? Do you delete it? Why?
Lets first have a look what C-style assertions are and why they are there. Assert is used for expressing a state that is always true. An assert cannot fail, if it fails then there is a bug in the code. So, C-style assert should and cannot be used for error handling. If the error is possible, then proper return value or exception error handling should be used, but if the error is impossible then this can be ensured and documented by using a C-style assert.
An extremely silly example is:
int a = 5;
assert (a > 0);
If this would fail, it would be a bug in the compiler.
C-Style asserts often go together with design by contract. In design by contract, we design the pre-condition, post-condition and class invariant to be per definition true. If it isn't true, then there is a bug in the caller in the case of breaking a pre-condition, a bug in the supplier in the case of breaking a post-condition or in the class in the case of breaking a class invariant. C-Style assert can be used to document this by, in the case of pre-condition, putting asserts at the beginning of a function. For example:
void doBlah(int x)
assert (x != 0);
So, this code reads that the doBlah can never be called with 0, if it does, then there is a bug in the function that calls doBlah.
Assert and unit tests
Oki, so now we know why the asserts are there, but how do you deal with them in unit tests?
Well.... you don't.
If we take the doBlah function above, we don't need to write a unit test for the doBlah to assert when x == 0. This is an impossible situation and it is not needed to test that. As said, assert is not error handling, it is error in programming :) You design (the contract) states that this is impossible and thus there is no need to test it.
Though, it ain't that easy. You do need to make sure the assert is really an assert. I often read code where the developer uses assert as a method for error handling (and he will regret that when the assert goes off in a production environment, though usually assert is commented out for production code). If assert is not assert, then you can delete it and add proper error handling.
Assert and test-driven code
Many years ago, I used to write a lot of asserts in my code. It was known as good style to make your contracts explicit. Today, I nearly never write an assert and tend to delete them from production code when I see them. Why?
First, I can't add the assert when I test-drive as it still is a line of code for which I don't have a test :) But that wouldn't be fair, as I *could* in theory test it (stub out the assert in the C-library). To better understand why I don't write them, we'll need to look at the purpose. An assert makes sure something can't happen and documents that. When test-driving code, we document how to use a piece of code in the tests. So, therefore the documentation aspect of tests causes the assert to be less useful. Also, the impossible situation often won't happen because I tested it, so I don't need to put the assert there.
So, what to do with C-Style asserts in existing code? I usually do either of these two things:
- Leave them and use the information as documentation
- Delete them, they clutter the code