Notes about Software Engineering, Test-Driven Development, C#.NET, Java and ...
Monday, October 12, 2009
LightContracts is a simple, small and lightweight library supporting 'Design by Contract'
Wednesday, February 11, 2009
What is Design by Contract?
This is the first article in a series which I started after I reasoned about "Why is Design by Contract not common practice in Software Enineering?".
Design by Contract is a technique to specify the behavior of a class. It helps to communicate what effects methods will have and what the methods expect before they can be executed. Design by Contract means that the caller of a class and the class itself make a contract, which is described as a set of assertions called Preconditions, Postconditions and Invariants.
So the interface and the implementation of a class is enhanced with additional assertions. Preconditions define obligations for the caller of a method, which have to be satisfied before the method is called. Postconditions will guarantee the outcome of the method. Invariants apply to the class as a whole and define conditions which are valid at the end of each method call.
I show an example in C# for preconditions and postconditions. Preconditions are commonly describes with the term 'require' and postconditions with the term 'ensure'. The property PrinterNames has the postcondition that the delivered list is not null. The method RemovePrinter has three preconditions:
public class Printers { private List<string> names; public Printers() { names = new List<string>(); names.Add("printer1"); names.Add("printer2"); } /// <summary> /// List of printer names. /// Assertion.Ensure(names != null,"Result is not null"); /// </summary> public List<string> PrinterNames { get { Assertion.Ensure(names != null,"Result is not null"); return names; } } /// <summary> /// Remove a printer /// Assertion.Require(PrinterNames().Count > 0,"There is at least one printer"); /// Assertion.Require(printerIndex >= 0,"printerIndex is not negative"); /// Assertion.Require(printerIndex lessThan PrinterNames().Count ,"printerIndex is in range"); /// </summary> public void RemovePrinter(int printerIndex) { Assertion.Require(PrinterNames.Count > 0,"There is at least one printer"); Assertion.Require(printerIndex >= 0,"printerIndex is not negative"); Assertion.Require(printerIndex < PrinterNames.Count ,"printerIndex is in range"); names.RemoveAt(printerIndex); } ... }
For further information about Design by Contract please refer to the Eiffel web site or the book Object-Oriented Software Construction
In the next article I will try to answer the question: What are the benefits of Design by Contract?
Wednesday, February 4, 2009
Why is Design by Contract not common practice in Software Enineering
- What is Design by Contract?
- What are the benefits of Design by Contract?
- Do I need to use Eiffel as my programming language to apply Design by Contract?
- Do I have to spend a lot of effort for Design by Contract?
- In which situations should I use Design by Contract?
- Does Design by Contract conflict with other design and implementation techniques like TDD and DDD?
Thursday, January 15, 2009
Preconditions, Postconditions: Design by Contract for C#
public static class Assertion { public static void Require(bool precondition, string conditionDescription) { if (!precondition) { throw new AssertException(ExceptionDescription("Precondition", conditionDescription)); } } public static void Require(bool precondition, string descriptionFormat, params object[] descriptionParameters) { if (!precondition) { throw new AssertException(ExceptionDescription("Precondition", string.Format(CultureInfo.InvariantCulture, descriptionFormat, descriptionParameters))); } } public static void RequireIsNotNull(object toBeTested,string objectName) { if (toBeTested==null) { throw new AssertException(ExceptionDescription("Precondition", objectName + " is not null")); } } public static void Ensure( bool postcondition, string conditionDescription ) { if ( ! postcondition ) { throw new AssertException(ExceptionDescription("Postcondition",conditionDescription)); } } public static void Ensure( bool postcondition, string descriptionFormat, params object[] descriptionParameters ) { if ( ! postcondition ) { throw new AssertException(ExceptionDescription("Postcondition",string.Format( CultureInfo.InvariantCulture, descriptionFormat, descriptionParameters ) ) ); } } public static void Check( bool condition, string conditionDescription ) { if ( ! condition ) { throw new AssertException(ExceptionDescription("Condition",conditionDescription)); } } public static void Check( bool condition, string descriptionFormat, params object[] descriptionParameters ) { if ( ! condition ) { throw new AssertException(ExceptionDescription("Condition",string.Format( CultureInfo.InvariantCulture, descriptionFormat, descriptionParameters ) ) ); } } // // Private methods // private static string ExceptionDescription(string assertionType, string description) { return string.Format(CultureInfo.InvariantCulture, "{0} failed. The expectation was '{1}', but this is false.", assertionType, description); } }As important as checking the assertions at runtime is to allow a client of our class to read the preconditions and postconditions without inspecting the implementation of our methods. The simplest solution is to copy the assertions into then method comments:
public class PrinterDescription { private XmlDocument printerXml; /// <summary> /// Load the descripton from a xml file /// Assertion.RequireIsNotNull(printerDescriptionPath, "printerDescriptionPath"); /// Assertion.Require(File.Exists(printerDescriptionPath), "File printerDescriptionPath exists"); /// Assertion.Ensure(IsLoaded, "IsLoaded"); /// </summary> public void Load(string printerDescriptionPath) { Assertion.RequireIsNotNull(printerDescriptionPath, "printerDescriptionPath"); Assertion.Require(File.Exists(printerDescriptionPath), "File printerDescriptionPath exists"); printerXml = new XmlDocument(); printerXml.Load(printerDescriptionPath); Assertion.Ensure(IsLoaded, "IsLoaded"); } /// <summary> /// Is the description loaded? /// </summary> public bool IsLoaded { get { return printerXml != null; } } /// <summary> /// Name of the printer /// Assertion.Require(IsLoaded, "IsLoaded"); /// </summary> public string Name { get { Assertion.Require(IsLoaded, "IsLoaded"); XPathNavigator nameNode = printerXml.CreateNavigator().SelectSingleNode("//PrinterName"); return nameNode.Value; } } }
Thursday, December 18, 2008
Preconditions, Postconditions, Invariants : Design by Contract for Java #2
public class Assertion { /** * Precondition. * @param condition we expect to be true. * @param description repeats the condition or describes it in * other words. */ public static void Require(boolean condition, String description) { if (!condition) { throw new RuntimeException("Precondition: The expectation '" + description + "' is violated"); } } /** * Precondition. * @param objectToBeTested is an object we expect to be not null * @param objectName is the name of the variable we test. */ public static void RequireNotNull(Object objectToBeTested,String objectName) { if (objectToBeTested == null) { throw new RuntimeException("Precondition: The expectation '" + objectName + " is not null' is violated"); } } /** * Postcondition. * @param condition we expect to be true. * @param description repeats the condition or describes it in * other words. */ public static void Ensure(boolean condition, String description) { if (!condition) { throw new RuntimeException("Postcondition: The expectation '" + description + "' is violated"); } } /** * Common condition to be used in the middle of methods * @param condition we expect to be true. * @param description repeats the condition or describes it in * other words. */ public static void Check(boolean condition, String description) { if (!condition) { throw new RuntimeException("Condition: The expectation '" + description + "' is violated"); } } }Why is the own implementation better than the assert statement?
- It does not make sense to switch off the runtime checks of assertions. We use assertions to simplify and avoid the error handling code in our methods. When it is possible to switch of assertions we loose this advantage, because we have to prepare our code to handle situations where the assertions are not checked.
- We are not only profiting from assertions during development and testing but also when the software is deployed. Assertions uncover error conditions much clearer and with better error descriptions. The logs in a production system will contain the messages from assertions and will help to find the cause of the problem much faster. If we switch off the assertions the problems will occurring much later and the cause of the problem can only be found with much more effort,
Monday, December 15, 2008
Mocking an object with two interfaces
public class TextManager { private IReader reader; public void readAndWrite() { String text = reader.read(); text = "*" + text + "*"; ((IWriter)reader).write(text); } }I wrote a unit test for the readAndWrite() method and recognized that I can't simply create a jMock object for the IReader/IWriter object. My colleague Henning found a simple an effective solution. In my test package I create a new interface called IReaderWriter which extends IReader and IWriter. Now I can create a mock object for this new interface which is able to work as a test double inside TextManager:
public void testReadAndWrite() { // Setup final IReaderWriter mock = context.mock(IReaderWriter.class); TextManager textManager = new TextManager(mock); context.checking(new Expectations() {{ one(mock).read(); will(returnValue("test")); one(mock).write("*test*"); }}); // Exercise textManager.readAndWrite(); // Verify context.assertIsSatisfied(); }
Thursday, November 27, 2008
Preconditions, Postconditions, Invariants : Design by Contract for Java
There are a lot of tools and framework which help you to apply DBC to your Java code. For example OVal, Contract4J, which are using AspectJ, iContract or Jass, which are using a preprocessor for Java.
In some circumstances it might be too much effort to apply one of these tools to you development project. Luckily there was a rudimentary support for testing assertions added to the Java language starting with release 1.4. The assert statement allows to define runtime checks inside your code. With some simple conventions you can benefit from DBC without heavy tool support. Here is an example how to use the assertion mechanism of Java to apply "Design by Contract":
/** * Is the editor in the edit mode? * @return True, if the editor is in the edit mode. Otherwise false. */ public boolean isInEditMode() { ... } /** * Sets a new text. * Precondition: isEditMode() * Precondition: text != null * Postcondition: getText() == text * @param name */ public void setText(String text) { assert isInEditMode() : "Precondition: isInEditMode()"; assert text != null : "Precondition: text != null"; this.text = text; assert getText() == text : "Postcondition: getText() == text"; } /** * Delivers the text. * Precondition: isEditMode() * Postcondition: result != null * @return */ public String getText() { assert isInEditMode() : "Precondition: isInEditMode()"; String result = text; assert result != null : "Postcondition: result != null"; return result; }So you can use "Design by Contract" effectively with three simple rules:
- Use the assert mechanism of Java to check preconditions, postconditions and invariants at runtime.
- Describe each assert with a string that starts either with "Precondition:", "Postcondition:" or "Invariant:" followed by the textual description of the assert expression.
- Add each assert description string to the javadoc comment of the method
public class Assertion { /** * Precondition. * @param condition we expect to be true. * @param description repeats the condition or describes it in * other words. */ public static void Require(boolean condition, String description) { if (!condition) { throw new RuntimeException("Precondition: The expectation '" + description + "' is violated"); } } /** * Precondition. * @param objectToBeTested is an object we expect to be not null * @param objectName is the name of the variable we test. */ public static void RequireNotNull(Object objectToBeTested,String objectName) { if (objectToBeTested == null) { throw new RuntimeException("Precondition: The expectation '" + objectName + " is not null' is violated"); } } /** * Postcondition. * @param condition we expect to be true. * @param description repeats the condition or describes it in * other words. */ public static void Ensure(boolean condition, String description) { if (!condition) { throw new RuntimeException("Postcondition: The expectation '" + description + "' is violated"); } } /** * Common condition to be used in the middle of methods * @param condition we expect to be true. * @param description repeats the condition or describes it in * other words. */ public static void Check(boolean condition, String description) { if (!condition) { throw new RuntimeException("Condition: The expectation '" + description + "' is violated"); } } }Using this class instead of 'assert' has the advantage that we can't forget to switch on the runtime checking of the assertions with -enableassertions. But keep in mind you still have to add method comments for the assertions. This is important, because the assertions should help the users of your class without forcing them to read your implementation details:
/** * Sets a new text. * Require: isEditMode() * Require: text != null * Ensure: getText() == text * @param name */ public void setText(String text) { Assertion.Require(isInEditMode(),"isInEditMode()"); Assertion.RequireNotNull(text, "text"); this.text = text; Assertion.Ensure(getText() == text,"getText() == text"); } /** * Delivers the text. * Require: isEditMode() * Ensure: result != null * @return */ public String getText() { Assertion.Require(isInEditMode(),"isInEditMode()"); String result = text; Assertion.Ensure( result != null, "result != null"; return result; }There is also an article "Using Assertions in Java Technology" on the Sun Developer network. See "Preconditions, Postconditions, Invariants : Design by Contract for Java #2" for further infos.