Wednesday, January 23, 2008

Design by Contract in more than 20 lines

Some time ago, Ayende wrote a post, Boo: Design By Contract in 20 lines. It shows what can be done with very little effort using Boo’s meta programming constructs.

I shamelessly ripped his code in order to take it a bit further. I wanted keywords that resemble those of Microsoft incubation DBC enabled language called spec#: Requires, ensures and invariant. There are more features in spec#, but these are the basic three.

I also wanted requirements to be places inside the method, not only as attributes outside of it. So here’s the code for download.

Heres a class that makes use of these constructs:

import DesignByContract
import System

[invariant(women > 0)] #Invariant declared outside of class still has access to fields
class Promise:
 eternalLife = false
 women = 8
 beer = "A lot"
 forgiven = true

 def SmellBad():
  women = 0

 [requires(beer == "Unlimited")] #Requirement outside of method scope
 def GetDrunk():
  beer = "none"
  ensures women > 3:   #Ensures statement that works on a block
   women = 4

 [ensures(eternalLife)] # Ensures statement outside of method scope
 def DuplicateCode():
  requires beer == "A lot" #Requires statement inside
  if forgiven:
   eternalLife = true

And finally, here’s a specification, or test if you will:

import Specter.Framework

context "Invariant, requires and ensures":
 promise as duck

  promise = Promise()

 specify { promise.SmellBad() }.Must.Throw()

 specify { promise.GetDrunk() }.Must.Throw()

 specify { promise.DuplicateCode() }.Must.Not.Throw()

The added requires, ensures and invariant statements in the Promise class act as a form of specification for the clients of the class. They communicate (and check runtime) what conditions are to be met if the output is to be valid. Now, the specification in the last code segment is written using Specter and it also acts as a specification for the class.

Note: If you haven’t tried Specter, which is a really nice DSL on top of NUnit, here’s what happens: Each context is made into a nUnit test class and each specify statement is translated into a test method, which can then be run using for instance the NUnit gui or with its own console runner. The syntax is way nicer than NUnit’s Assert.ThisAndThat(…) and the test methods are automatically named.

Both the internal specification in the form of requires, ensures and invariant statements and the external specification embodied in the specter context (unit test) tell us something about how the system is expected to behave under certain conditions.

When to use which then? Good question, glad you asked. I haven’t thought about that for very long, but perhaps you guys have an idea? I suppose the main difference is that the external specification is run on demand, like a test. The internal specification is run every time the method is run, in production as well. The internal specification has better be relatively cheap compared to the external one. So, any thoughts?

No comments:

Post a Comment