The organization really goes like this:
Cobra has
contracts which include:
-- invariants
-- require / pre-condition
-- ensure / post-condition
These are boolean conditions that are checked at run-time during the execution of methods of objects.
Cobra has
unit tests which include:
-- class level unit tests
-- method level unit tests (as well as properties, initializers, etc.)
Both contracts and unit tests are intended to improve software quality more rapidly than if they were not used. They have their pros and cons.
Contracts are more definition-oriented. When you can define your method in terms of boolean expressions, it can be very powerful. For one thing, the contracts execute the entire time your software is running which is extremely thorough. Also--and I'm having a hard time finding the words for this--there is an aspect of "completeness" when you can define your methods. You can forget a test case, but a definition is a definition.
Contracts also define the interface/behavior of the class.
However, unit tests can often be easier to write than contracts. And they show examples of using a method or object that are typically easier to learn from than contracts, particularly in more complex situations.
Because software quality continues to be a big issue, I am not a fan of adopting one technique and investing all my hopes in it. Cobra offers multiple features described in
"Coding for Quality".
Regarding invariants, which was your question, here is a simple example (not tested or even compiled):
class Customer
enum Status
Good
PastDue
invariant
.balance > 0 implies .status == Status.PastDue
.balance <= 0 implies .status == Status.Good
pro balance from var as decimal
get status as Status
if .balance > 0, return Status.PastDue
else, return Status.Good
In that example, the invariant shows you what to expect regarding the properties of .balance and .status. Because it's expressed in code, it's for real (documentation can drift out of sync with the actual code and often does). Because it's based on succinct boolean expressions, it's fairly easy to read and understand. Because it's an invariant, it will be checked constantly at run-time and you will find out as early as possible if the software violates it. Early detection often reduces the amount of mental effort required to figure out where things first went wrong.
I tried to help by being extra thorough here. I hope it did.