On 30Sep2018 12:17, Chris Angelico email@example.com wrote:
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 firstname.lastname@example.org