Jonathan M Davis:

> I generally end up using unit tests to verify that stuff works correctly and 
> then 
> throw exceptions on bad input. So while I like having DbC built in, I don't 
> end 
> up using it all that much. It's prim,arily invariant that I end up using 
> though, 
> and that's harder to do inside of the member functions.

I think the problem here is that you are not using your D tools well enough yet:
- Preconditions allow you to save some tests in your unittests, because you 
have less need to test many input boundary conditions.
- Postconditions are useful to save some code to test the correctness of the 
results inside your unittests. You still need to put many tricky but correct 
input conditions inside your unittests, but then the postcondition will test 
that the outputs are inside the class of the correct outputs, and you will need 
less unittest code to test that the results are exactly the expected ones in 
some important situations.
- The missing "old" feature once somehow implemented allows to remove some 
other unittests, because you don't need a unittest any more to test the output 
is generally correct given a certain class of input.
- Class/struct invariants do something unittests have a hard time doing: 
testing the internal consistency of the class/struct data structures in all 
moments, and catching an inconsistency as soon as possible, this allows to 
catch bugs very soon, even before results reach the unittesting code. So their 
work is not much duplicated by unit testing.
- Loop invariants can't be replaced well enough by unittests. Again, they help 
you find bugs very early, often no more than few lines of code later of where 
the bug is. Unittests are not much able to do this.
- Currently you can't use invariants in some situations because of some DMD 
bugs.
- You must look a bit forward too. If D will have some success then some person 
will try to write some tool to test some contracts at compile time. 
Compile-time testing of contracts is useful because it's the opposite of a 
sampling: it's like an extension of the type system, it allows you to be sure 
of something for all possible cases.

Unit tests are useful as a sampling mean, they allow you to assert your 
function does exactly as expected for some specific input-output pairs. DbC 
doesn't perform a sampling, it tests more general properties about your inputs 
or outputs (and if you have some kind of implementation of the "old" feature, 
also general properties between inputs and their outputs), so DbC testing is 
wider but less deep and less specific than unit testing.

Generally with unittesting you can't be sure to have covered all interesting 
input-output cases (code coverage tools here help but don't solve the problem. 
Fuzzytesting tools are able to cover other cases), while with DbC you can't be 
sure your general rules about correct inputs, correct outputs (or even general 
rules about what a correct input-output pair is) are accurate enough to catch 
all bad situations and computations.

So generally DbC and unittests are better for different purposes, using them 
both you are able to complement each other weak points, and to improve your D 
coding.

I also suggest to try to write contracts first and code later, sometimes it 
helps.

Bye,
bearophile

Reply via email to