Test Driven Design by Contract

Laatst hoorde ik iemand beweren dat in statisch getypeerde talen Design by Contract (DbC) een superieur alternatief is voor Test Driven Design (TDD). Mijn standpunt was andersom: TDD is veel meer dan alleen unit testen en zelfs unit tests zijn niet volledig vervangbaar door DbC. Ik heb de belangrijkste argumenten in de discussie nog eens bij elkaar gebracht in deze post. Het is zeker geen volledig en uitgediept artikel, maar ik hoop dat het toch genoeg aanknopingspunten bevat om op te reageren. Ik ben benieuwd wat anderen hiervan denken.

Ik zie DbC meer als een alternatief voor documentatie. Waar een ontwikkelaar normaal gesproken pre- en postcondities in de documentatie dient aan te geven, kan hij dat met DbC technieken direct op een formele manier beschrijven, te controleren door de compiler. Best cool! Maar, wat ik tot nu toe gezien heb van bijvoorbeeld Spec#, maakt dat ik me afvraag of het niet misschien nog wat te vroeg is om de techniek in C# te gebruiken. Het verbruikte best veel processor capaciteit. Maar goed, misschien zijn andere implementaties sneller. En ik heb nog niet gehoord van snelheidsproblemen bij het gebruik van DbC in Erlang.

Goed, tijd om mijn zaak ter verdediging van TDD te openen. Ik zal een aantal PRO’s voor het gebruik van TDD als techniek eerst behandelen en dan de PRO’s van DbC, allebei waar toepasselijk, vergeleken met de ander. Praktische bezwaren zoals snelheidsproblemen laat ik er buiten, die lijken me oplosbaar.

PROs TDD

  1. TDD zorgt ervoor dat je je eerst focust op de interface van wat je aan het 0ntwikkelen bent. Dit helpt een ontwikkelaar om een elegante interface te bedenken dat weinig overhead oplevert voor de client code. Zoiets zie ik niet gebeuren bij het gebruik van DbC.
  2. Unit tests testen niet alleen het contract van een method, maar ook zijn implementatie. Dit lijkt me wel belangrijk.
  3. Geautomatiseerde unit tests geven je een vrijwel intantaan zicht op de problemen in je code, net als DbC je dat geeft d.m.v. compiler errors (of vergelijkbaar).

PROs DbC

  1. DbC helpt een ontwikkelaar continue bij het gebruik van een API. Unit tests moet je eerst hebben en je moet er ook in kijken, voordat ze je helpen bij het gebruik van een API (ik ga dat niet vertalen met consumeren).
  2. DbC helpt een ontwikkelaar om minder defensief te programmeren. Argument controle en Excepties worden vervangen door pre- en postcondities, die compile time gecontroleerd worden.
  3. DbC helpt je om pre- en postcondities in een consistente en nuttige manier vast te leggen. Als je ervan uitgaat dat je ze toch moet schrijven, dan kost dat dus geen extra tijd. Het schrijven van unit tests is een investering van ontwikkeltijd die je niet heel direct terugverdient. [Dit argument vind ik nogal discutabel, omdat een grote hoeveelheid API documentatie geen pre- en postcondities bevat. Zou dus wel eens niet zo nuttig kunnen zijn als DbC enthousiastelingen denken en kost bovendien dus wel extra tijd. En het schrijven van tests is een bewezen manier om de kwaliteit van je code te verhogen en daarmee dus tijd te winnen doordat minder debugging noodzakelijk is]

Zoals je kunt zien blijven de positieve effecten van TDD en DbC overeind in vergelijking met elkaar. Dus ik denk dat TDD and DbC geen vervanging van elkaar zijn. Er is enige overlap. Je zou misschien een aantal unit tests kunnen laten vallen die je zonder DbC zou schrijven om de afhandeling te testen van argumenten die buiten de normale grenzen gaan. Maar over het algemeen zie ik ze nu vooral als elkaar aanvullend.

DbC helpt om een methode vollediger te beschrijven dan een naam ooit kan doen en op zo’n manier dat er compile time gechecked kan worden op fouten. Maar volgens mij schrijf je documentatie pas als het ontwerp van je code begint te stabiliseren. Voor code die nog veel verandert, is documentatie alleen maar een vertragende factor bij het refactoren. TDD focust je aandacht op het schrijven van code die goed te gebruiken is, een API die goed te gebruiken is en levert je meteen unit tests op. Natuurlijk leveren unit tests ook wel wat vertraging op bij het refactoren, maar leveren je daarbij echter ook heel veel tijd op doordat je weet dat je code nog werkt. Bovendien focus je meer op de bedoeling van een methode of class, wat het minste zou moeten veranderen. Als je goede unit tests schrijft.

Tenslotte ben ik niet de enige die denkt dat TDD en DbC elkaar aanvullen: Het artikel over TDD op Wikipedia vermeldt hetzelfde. En hier is een artikel dat hetzelfde beschrijft, waarin zelfs een nieuwe stijl van ontwikkelen daarmee in het leven wordt geroepen, genaamd Agile Specification-Driven Development.