Design-by-Contract (DbC) v Test-Driven Design (TDD)

A software contract in the Eiffel language

Another bit of software engineering knowledge from my archive relates to two well-known formal quality methods used in software development. This is from a presentation made at ETH Zurich in 2010.

The background to this is my use of the Eiffel language for 25 years, in which Design by Contract (DbC) figures strongly. Its creator, Bertrand Meyer (currently provost Chair of Software Engineering at Schaffhausen Institute of Technology, Switzerland) coined the term, and developed DbC from earlier formulations such as in the Z language, Djikstra etc.

For those not familiar with ‘contracts’, the full form consists of routine pre- and post-conditions and class invariants. An example of a contract is shown below:

fill is
		-- Fill tank with liquid
	require
		in_valve.open
		out_valve.closed
	deferred
		-- i.e., no implementation
	ensure
		in_valve.closed
		out_valve.closed
		is_full
	end

The contract consists of the require and ensure predicates. The Eiffel website has a full explanation.

DbC was built into Eiffel (since 1988), and has some native supported in some languages like Ada 2012, Clojure, and Kotlin. Many other languages have had additional frameworks or tools created to add contracts, e.g. Java, C#, Groovy and so on.

However, it has to be said that the culture of contract-oriented has not become mainstream, which those of us who use them routinely find strange – they catch so many errors in testing that it is clear they greatly add to the quality of software.

Some experts think that Test-Driven Design (TDD) is a better alternative, since can be used to force developers to prove that each function they write passes an appropriate test, before they write more code. Personally, although I think routine use of unit testing via built-in frameworks is useful, TDD doesn’t replace DbC, and indeed, it can lead some developers to think their software, if passing all its tests, is ‘done’.

Understanding the difference between DbC and TDD is instructive to understanding why TDD, although very useful, doesn’t tell you if your software is correct.

Here’s the basic difference:

  • A DbC contract is a mathematical specification of the valid domain (input state space) and/or result (output state space) of a routine within a class;
  • TDD is development using ‘tests’ that constitute specific points in a routine’s input value space, which test a routine on an instance.

Mathematically, contracts are intensional (i.e. formally stated) definitions of valid state spaces. Tests are a kind of extensional definition in that they form a list of particular input or output values. From tests on their own, it is hard to a) determine what the total valid value space is and b) hard to see the design intent of the routine.

Since you still do testing if you are using DbC, DbC = contracts + tests. If you are using TDD only, you are just using tests – you have no formal statement of the intended valid state space of input values or results. It is therefore a somewhat haphazard exercise to know what tests to write, especially against someone else’s code. How do you know if you have sufficient coverage? Answer: you don’t.

Indeed, the presence of contracts is an important indicator for writing new tests, since the contract is telling you which kinds of values or states will make the software execute correctly or not.

I remain mystified as to why they are not built into more languages.

About wolandscat

I work on semantic architectures for interoperability of information systems. Much of my time is spent studying biomedical knowledge using methods from philosophy, particularly ontology and epistemology.
This entry was posted in Computing. Bookmark the permalink.

8 Responses to Design-by-Contract (DbC) v Test-Driven Design (TDD)

  1. wolandscat says:

    My good friend Andrew Reilly post the following comment, which was mysteriously rejected. So let’s try a bit of post-delivery testing right here:

    [Andrew said:]
    Doesn’t help with specification bugs, unfortunately. I’m willing to bet that utcoffset isn’t an integer in any working system in South Australia…

    The corollary to the old saw that “you can write Fortran in any language” is that you can write bugs in any language. No matter how much correctness proofing or test harnesses you have, you can always implement the wrong algorithm.

  2. Andrew Reilly says:

    Testing, testing. Is this thing on?

    • Andrew Reilly says:

      Yup. Intermittent failure is the hardest thing to debug. Perhaps we’ll never know the cause…

  3. Michael Miller says:

    Tom

    Would appreciate a Zoom call sometime if possible on semantic interoperability and openEHR.

    Is this possible?

    Michael

    *Prof. Michael Miller C.Eng, CITP, FBCS, FEDIPLdgPra*

    *CEO*

    *The Care Innovation Corporation*

    *UK Mobile:* +447973626260

    *Website:* http://www.cic.bio

  4. Misty says:

    The way I think about it and practice – unit tests are for public api/contract/specification (meant to test behavior) while DBC are for internal/private logic (I actually use assertions to achieve that).. Don’t you agree?

    • Aurimas says:

      Isn’t it more the reverse though? With unit tests we test units, which people often mistake of meaning 1 class. Instead, units can be a collection of classes that make up a logical ‘unit’ (1 or more classes).
      However, it’s pretty hard to ensure correct behaviour of a whole system only by unit testing.
      If I understand correctly, design by contract is more thinking about the system as a whole, more like acceptance tests, if that makes sense.
      Or, thinking in other terms, assuring that functional requirements of a system are fulfilled.

  5. David Kerr says:

    I have Meyer’s OOSC in my library from years ago. I like the idea but there’s native support in Java or C++. I come here today in exasperation at the sheer volume of test code to production code. People say “read the tests to understand the code” and I shiver inside a little everytime I hear that – I want to read and understand the production code; the test code is just to test it. DbC looks so good on paper – “everything” is in the production code – if you trust the DbC assertion logic then you should need a LOT less test code. Every time I see a pull-request I die a little at the prospect of wading through pages of unit tests.

    DbC coupled with making illegal states unrepresentable (ADT’s, no nulls, etc) would be a dream.

    Now back to that merge-request ;(

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s