On 30Sep2018 12:17, Chris Angelico
At the moment, I'm seeing decorator-based contracts as a clunky version of unit tests. We already have "inline unit testing" - it's called doctest - and I haven't seen anything pinned down as "hey, this is what it'd take to make contracts more viable". Certainly nothing that couldn't be done as a third-party package. But I'm still open to being swayed on that point.
Decorator based contracts are very little like clunky unit tests to me. I'm
basing my opinion on the icontracts pip package, which I'm going to start
using.
In case you've been looking at something different, it provides a small number
of decorators including @pre(test-function) and @post(test-function) and the
class invariant decorator @inv, with good error messages for violations.
They are _functionally_ like putting assertions in your code at the start and
end of your functions, but have some advantages:
- they're exposed, not buried inside the function, where they're easy to see
and can be considered as contracts
- they run on _every_ function call, not just during your testing, and get
turned off just like assertions do: when you run Python with the -O
(optimise) option. (There's some more tuning available too.)
- the assertions make qualitative statements about the object/parameter state
in the form "the state is consistent if these things apply";
tests tend to say "here's a situation, do these things and examine these
results". You need to invent the situations and the results, rather than
making general statements about the purpose and functional semantics of the
class.
They're different to both unit tests _and_ doctests because they get exercised
during normal code execution. Both unit tests and doctests run _only_ during
your test phase, with only whatever test scenarios you have devised.
The difficulty with unit tests and doctests (both of which I use) and also
integration tests is making something small enough to run but big/wide enough
to cover all the relevant cases. They _do not_ run against all your real world
data. It can be quite hard to apply them to your real world data.
Also, all the @pre/@post/@inv stuff will run _during_ your unit tests and
doctests as well, so they get included in your test regime for free.
I've got a few classes which have a selftest method whose purpose is to confirm
correctness of the instance state, and I call that from a few methods at start
and end, particularly those for which unit tests have been hard to write or I
know are inadequately covered (and probably never will be because devising a
sufficient test case is impractical, especially for hard to envisage corner
cases).
The icontracts module will be very helpful to me here: I can pull out the
self-test function as the class invariant, and make a bunch of @pre/@post
assertions corresponding the the method semantic definition.
The flip side of this is that there's no case for language changes in what I
say above: the decorators look pretty good to my eye.
Cheers,
Cameron Simpson