Mittwoch, 28. Dezember 2011

Principle of Least Astonishment

A fundamental rule of software development is building small assemblies with high cohesion. Software assemblies do one job and they have a clear name describing this job. This means, if you are not able to give assemblies a clear name, you have to split the assemblies to even smaller ones. Breaking this rule will violate the Principle of Least Astonishment and annoy the users of the software assemblies. In bad cases, wrong usage and bugs follow simple code smells which could be solved by clear naming or the separation of the assemblies.

The following method is an example of a code smell violating the Principle of Least Astonishment. This software shows that it is possible to break a source code base with a few lines of smelly code.

Principle of Least Astonishment violation:

public void addInstance(String className) {

    try {

       Class clazz = Class.forName(className);
       objectSet.add(clazz.newInstance());
    }
    catch (RuntimeException ex) {

       throw ex;
    }
    catch (Exception ex) {

      throw new RuntimeException(ex);
    }
}

What is wrong with this code?

  1. There are no asserts in the code to check pre and post conditions. A stable implementation would check the method argument and type of the created instance with “instanceof” (be careful you would on the other hands side lose the flexibility of the implementation and violate the Liskov Substitution Principle).

  2. The compile time type checking is lost. Therefore, errors may come up late at runtime, which hit the code hard and open the door for a security violation.

  3. The more stable implementation of the code would lead to code bloat.

  4. The method name is bad. There is more than adding an instance. The better name is “createAndAddInstance”. Think about the name. The possible better solution is to split the method instead of changing the name.

  5. The implementation assumes that instances which the access modifiers "public" or "package" are created and added to the set.
An alternative implementation covers the strong points and shows the code bloat to remember “new” with all its benefits could be more appropriate solving the main problems of the implementation.

Alternative implementation including an Unit-Test:

import java.util.HashSet;
import java.util.Set;
import org.ccd.reflection.test.DomainObjectException.ErrorCode;
import org.junit.Assert;
import org.junit.Test;

public class InstantiationByReflectionTest {
   
    private Set<DomainObject> objectSet = new HashSet<DomainObject>();
       
    @Test
    public void testInstantiation() {
   
        createAndAddDomainObject("org.ccd.reflection.test.DomainObject");
        createAndAddDomainObject("org.ccd.reflection.test.DomainObject");
       
        Assert.assertTrue(objectSet.size() == 2 ? true : false);   
    }
       
    @Test
    public void testNullPointerArgument() {
   
        try {
       
            createAndAddDomainObject(null);
        }
        catch(DomainObjectException ex) {
           
            Assert.assertTrue(ex.errorCode() == ErrorCode.NullPointerArgument);
        }
    }
   
    @Test
    public void testEmptyArgument() {
   
        try {
       
            createAndAddDomainObject("   ");
        }
        catch(DomainObjectException ex) {
           
            Assert.assertTrue(ex.errorCode() == ErrorCode.EmptyArgument);
        }
    }
   
    @Test
    public void testIllegalType() {
   
        try {
       
            createAndAddDomainObject("java.lang.String");
        }
        catch(DomainObjectException ex) {
           
            Assert.assertTrue(ex.errorCode() == ErrorCode.IllegalType);
        }
    }
   
    @Test
    public void testClassNotFound() {
   
        try {
       
            createAndAddDomainObject("org.ccd.reflection.test.MissingDomainObject");
        }
        catch(DomainObjectException ex) {
           
            Assert.assertTrue(ex.errorCode() == ErrorCode.ClassNotFound);
        }
    }
   
    @Test
    public void testInstantiationFailure() {
   
        try {
       
            createAndAddDomainObject("java.io.Serializable");
        }
        catch(DomainObjectException ex) {
           
            Assert.assertTrue(ex.errorCode() == ErrorCode.InstantiationFailed);
        }
    }
   
    @SuppressWarnings("unused")
    private class MyDomainObject {}

    @Test
    public void testInstantiationPrivate() {
   
        try {
       
            createAndAddDomainObject("org.ccd.reflection.test.MyDomainObject");
       
        }
        catch(DomainObjectException ex) {
           
            Assert.assertTrue(ex.errorCode() == ErrorCode.ClassNotFound);
        }   
    }

    private void createAndAddDomainObject(final String className) {
   
        assertNotNull(className);
        assertNotEmpty(className);
       
        Class<?> cl = null;
        try {
       
            cl = Class.forName(className);
        }
        catch (ClassNotFoundException ex) {
       
            throw new DomainObjectException(ErrorCode.ClassNotFound);
        }
              
        Object domainObject = null;
        try {
           
            domainObject = cl.newInstance();
        }
        catch (InstantiationException ex) {
           
            throw new DomainObjectException(ErrorCode.InstantiationFailed);
        }
        catch (IllegalAccessException ex) {
       
            throw new DomainObjectException(ErrorCode.IllegalAccess);
        }
      
        assertDomainObjectType(domainObject);
       
        objectSet.add((DomainObject) domainObject);
    }
   
    private void assertNotNull(final String className) {

        if(null == className) {
           
            throw new DomainObjectException(ErrorCode.NullPointerArgument);
        }
    }
   
    private void assertNotEmpty(final String className) {

        if(className.trim().isEmpty()) {
           
            throw new DomainObjectException(ErrorCode.EmptyArgument);
        }
    }
   
    private void assertDomainObjectType(final Object domainObject) {
       
        if(!(domainObject instanceof DomainObject)) {
           
            throw new DomainObjectException(ErrorCode.IllegalType);
        }
    }
}

class DomainObject {}

class DomainObjectException extends RuntimeException {
   
    public enum ErrorCode {
       
        ClassNotFound("Class not found!"),
        InstantiationFailed("Instantiation failed!"),
        IllegalAccess("Access not denied!"),
        NullPointerArgument("Parameter cannot be null!"),
        EmptyArgument("Parameter cannot be empty!"),
        IllegalType("Illegal type detected");
       
        private final String errorMessage;
       
        private ErrorCode(final String errorMessage) {
           
            this.errorMessage = errorMessage;
        }

        String errorMessage() {
       
            return errorMessage;
        }
    }
   
    private final ErrorCode errorCode;
   
    public DomainObjectException(final ErrorCode errorCode) {           
       
        this.errorCode = errorCode;
    }

    ErrorCode errorCode() {
   
        return errorCode;
    }
}


Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.
 

Freitag, 8. Juli 2011

Java 7 Spike

Oracle released the JDK developer preview for Java SE 7. This release is feature complete, stable and ready for testing. These were enough reasons to write a short Java SE 7 Spike. The first step writing the Spike was to install the JDK. After the installation of the JDK, I installed Eclipse Indigo for Java Developers and a patch to enable the Java 7 compiler compliance level in Eclipse. The Spike involves small tests to gain experience and knowledge about Java 7 language enhancements which are hosted in the Coin Project.

The Spike covers the following Java 7 features:

  • strings in switch statements
  • underscores in numeric literals
  • catching multiple exceptions
  • rethrow exceptions with improved type checking
  • type inference for generic instance creation (Diamond)
  • try-with-resources statement

Java 7 Spike:

import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.ParseException;
import java.util.LinkedList;
import java.util.List;
import org.junit.Test;

public class Java7Spike {

    @Test
    public void testStringsInSwitch() {
       
        assertEquals("right direction", decideDirection("right"));
        assertEquals("left direction", decideDirection("left"));
        assertEquals("no direction", decideDirection("no"));
    }

    private String decideDirection(final String decision) {
       
        switch(decision) {
       
            case "right":
               
                return "right direction";
           
            case "left":
               
                return "left direction";
           
            default:
               
                return "no direction";   
        }
    }
   
    @Test
    public void testUnderscoreInNumericLiterals() {
       
        int numericLiteral = 12_30;
        numericLiteral++;
       
        assertEquals(1231, numericLiteral);
    }
   
    @Test
    public void testMultiCatch() {
       
        try {
           
            final String fileText = readFile("src/org/ccd/java/features/testNio.txt");
            parseFile(fileText);           
        }
        catch (IOException | ParseException ex) {
           
            throw new IllegalStateException("Error during file handling occured!", ex);
        } 
    }
   
    private String readFile(final String filename) throws IOException {return null;}
    private void parseFile(final String content) throws ParseException {}
   
    @Test(expected=FirstException.class)
    public void testRethrowExceptionWithTypeChecking()
                                          throws FirstException, SecondException {       
        try {
           
            simulateFailure();
        }
        catch (Exception ex) {
         
            throw ex;
        }         
    }

    private void simulateFailure() throws FirstException {
       
        throw new FirstException();
    }

    private class FirstException extends Exception {
       
        private static final long serialVersionUID = -1872200400984813924L;
    }
   
    private class SecondException extends Exception {
       
        private static final long serialVersionUID = 7453507163657978623L;
    }
   
    @Test
    public void testDiamond() {
   
        final List<String> list = new LinkedList<>();
        list.add("anEntry");
       
        assertEquals(1, list.size());
    }
   
    @Test
    public void testTryWithResources() throws IOException {
       
          final Path file = Paths.get("src/org/ccd/java/features/testNio.txt");
         
          try (AsynchronousFileChannel channel = AsynchronousFileChannel.open(file)) {

              final ByteBuffer buffer = ByteBuffer.allocate(100_000);

              channel.read(buffer, 0, buffer, new CompletionHandler<Integer, ByteBuffer>() {

                  public void completed(Integer result, ByteBuffer attachment) {
                 
                      assertEquals(19, result.intValue());
                  }
   
                  public void failed(Throwable exception, ByteBuffer attachment) {
                 
                      exception.printStackTrace();
                  }               
              });
         }
    }
}

I am looking forward to Java SE 8 which may include further language enhancements, summarized in the Coin Project and additional features like the integration of Closures (JSR 335 / Lambda Project).


Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.

Montag, 4. Juli 2011

Catch the web service bug

Agile developer catch bugs with tests. Unit tests improve the internal quality of a software system and acceptance tests the external quality visible to the users. The testing effort reduces the defect rate and increases maintainability. Smart testing is a shield against the trap of slow and high cost development that could occur by poor written code which is not covered by tests. The fast feedback of the tests gives developers confidence and more time for other tasks in the software development cycle.

But take care! Test driven development (TDD) including high test coverage is not the guarantee to pass all possible problems. There is the danger to walk in a bad direction forgetting the big picture during refactoring. This is also true working in the flow zone, forgetting the picture which the software model defines. Some refactoring steps following KISS and pragmatic programming could possibly drive the ball also in a new direction, which could be in the future a problem while extending the software due to a change request.

Agile developers are careful and aware of such traps. TDD should start at the major components. The model of a software solution is a typical candidate for TDD. This is also true for web services which are simply backend services with a remote interface accessible via the internet. This sounds simple, but is it simple? There is a network between the backend service and the user of the web service. Networks may be slow, sometimes the connection is missing and the world wide web is like the deep universe. A good reason to limit the access to web services and secure them as strong as possible. Securing web services and fulfill all the non-functional requirements needs time and can’t be covered with a simple unit test. Special testing is necessary beneath the unit and acceptance tests. Agile developers know that they have to have a test pyramid in the pocket!

The lowest levels of the pyramid are unit and acceptance tests. From the developer perspective the aim to catch bugs should be to find them as early as possible. Perfectly with unit tests not using additional acceptance test frameworks. Java SE 6 gives web service developers an endpoint publisher that offers the possibility to test end to end in simple unit tests. This statement fits for SOAP- and REST-based web services which are based on JAX-WS. 

Here comes a little story about the competition of SOAP- and REST-style web services:

The opinion that REST-style web services are the vehicle which solves a lot of problems easily and pretty lightweight flows around in the community. I’ve heard that often. Few days ago, I read a statement in a Java EE blog and wrote a few lines:

JAX-WS is a mature technology for building SOAP- and REST-based web services. The REST part of JAX-WS is not comfortable enough for web service developer, therefore JAX-RS was borne which is easier to use even with more functionality. But this is not the point! 

The point is that JAX-WS (SOAP part) and JAX-RS are both legal depending on use cases. The real benefit of SOAP-based web services is the WSDL which defines the web service interface. The top-down approach (WSDL2Java) is still a best practice to solve interoperability problems. WSDL interfaces are clear and (quasi) normed by using the WS-I basic profile as a guide for web service development. 

The WSDL is the important artifact from which the generation of Java artifacts is easy by tooling. The power of WSDL is important in Enterprise Service Bus (ESB) environments and Java Business Integration (JBI) components, which are described by WSDL. My opinion is that REST is not the big player in ESB environments. I think you need a definition (for SOAP services the WSDL) to handle parameter types and encodings, protocol transformations, routings and mediation. Therefore, I see REST rather as the lightweight and maybe agile gate to portal backend systems to build single processes (no orchestration necessary).

I fully agree that JAX-RS makes coding funny and I like to write RESTful web services rather to write a complex WSDL file (although Eclipse support is available). The lightweight approach of REST is nice, but has a danger. The input line of the browser (you need no scripting to call the service) is the direct gateway to the database for a not properly secured REST service. To secure the REST service is still effort, including configuration and programming tasks.


The response came very fast: Which use cases?!

The simplest use case is the usage of an existing session facade. The publication of the facade as web service is easy by adding an annotation (@WebService). More fine granular by using annotations on the facade methods (@WebMethod). This use case is not really a strong point, but doing the same with REST is more coding and design effort for the simple goal to use the legacy software as web service.

A stronger point is that with JAX-WS the developer defines domain specific interfaces (domain specific verbs) which are self-describing by the WSDL. The REST interface instead is generic (set of defined verbs) leaving the domain specific subjects in the addressed resources. This is essential! JAX-WS (SOAP-part) is therefore primarily activity-oriented and REST is strongly resource-oriented. The architecture of REST is based on the generic interface for mainly CRUD operations. You see it also in the Response and Fault codes. SOAP faults are domain specific. REST faults are Http specific error codes. To use the response for the domain specific fault message in association with Http state 200 (OK) is a poor decision. This leads to a possible use case were domain specific faults has to be traced and processed (JAX-WS - SOAP fault has a benefit!).


Developer can build complex interactions with REST. Even multipart messages are possible with JAX-RS. The argument that REST is inherently stateless and SOAP has the possibility to define stateful services can be moderated by defining a stateless architecture not holding any state on the server side. On the other side, I see some bricks to lay out use cases.


When we come to ESBs the strong points occur. SOAP services can be bound to different protocols. ESBs work internally with JMS to cover the requirements of the pipeline architecture. Without the possibility to bound SOAP to different protocols this architecture which is a key point for SOA wouldn’t as it is today. The SOAP extensibility model is the key, defining SOAP nodes processing the header of the SOAP envelope for routing and mediation issues. We do not have such functions for REST. REST is easy but tightly coupled with the Http protocol. I do not actually see the orchestration and workflow requirements covered by REST where human tasks are involved and the reply is minutes, sometimes hours or days away from the primary request.

I told you that short story that you have a feeling for
the effort of writing the SOA- and REST-parts which are published in the following unit test with the endpoint publisher. You see in the unit tests how easy it is to publish a SOAP-based web service from an existing model and the additional effort to use the existing model in a REST-based web service. I admit that the RESTful web service is a Java EE 5 one. Java EE 6 and JAX-RS makes the implementation of RESTful web services simpler.

The backend model of the web service is a quiz application with two simple operations. I show only the unit tests of the web service backend model and the web service test parts. The implementation of the backend service is not interesting in this context. My goal is to show the endpoint publisher in action and not the implementation of simplified web services.

Web Service backend model test:

@RunWith(value=Parameterized.class)
public class QuizServiceTest {

    private String[][] quizData;
    private final String question;
    private final String answer;
   
    private Quiz quizService;
   
    public QuizServiceTest(final String[] questions,
                                      final String[] answers) {
       
        this.question = questions[0];
        this.answer = answers[0];
       
        initializeQuizData(questions, answers);
    }

    private void initializeQuizData(final String[] questions,
                                               final String[] answers) {
       
        quizData = new String[1][2];
        quizData[0][0] = questions[0];
        quizData[0][1] = answers[0];
    }
   
    @Parameters
    public static Collection<String[][]> getTestParameters() {
      
        return Arrays.asList(new String[][][] {
              
                {{"firstQuestion"},{"firstAnswer"}},
                {{"secondQuestion"},{"secondAnswer"}}});
    }
   
    @Before
    public void setUp() {
       
        quizService = new QuizServiceController();
        quizService.initialize(quizData);       
    }
   
    @Test
    public void testQuizCorrectAnswer() {

        assertEquals(true, quizService.answer(question, answer));
    }
   
    @Test
    public void testQuizWrongAnswer() {
           
        assertEquals(false, quizService.answer(question, "anAnswer"));
    }
}

SOAP-Part unit test using the endpoint publisher:

@RunWith(value = Parameterized.class)
public class QuizSOAPServiceEndToEndTest {

    private static final String PUBLISH_URL = "http://127.0.0.1:8080/quiz";
    private static final String WSDL_URL = "http://127.0.0.1:8080/quiz?wsdl";
    private static final String QNAME_NAMESPACE_URL = "http://quiz.service.web.java.ccd.org/";
    private static final String QNAME_SERVICE_NAME = "QuizServiceService";
   
    private String[][] quizData;
    private final String question;
    private final String answer;
   
    private QuizServiceSOAP quizService;

    public QuizSOAPServiceEndToEndTest(final String[] questions,
                                                              final String[] answers) {

        this.question = questions[0];
        this.answer = answers[0];

        initializeQuizData(questions, answers);
    }

    private void initializeQuizData(final String[] questions,
                                               final String[] answers) {
       
        quizData = new String[1][2];
        quizData[0][0] = questions[0];
        quizData[0][1] = answers[0];
    }

    @Parameters
    public static Collection<String[][]> getTestParameters() {

        return Arrays.asList(new String[][][] {

                {{"firstQuestion"},{"firstAnswer"}},
                {{"secondQuestion"},{"secondAnswer"}}});
    }

    @Before
    public void setUp() throws MalformedURLException {

        publishQuizService();       
        lookupQuizService();       
        initializeQuizService();
    }

    private void publishQuizService() {
       
        Endpoint.publish(PUBLISH_URL, new QuizService());
    }

    private void lookupQuizService() throws MalformedURLException {
       
        final URL url = new URL(WSDL_URL);
        final QName qname = new QName(QNAME_NAMESPACE_URL,QNAME_SERVICE_NAME);
        final Service service = Service.create(url,qname);
        quizService = service.getPort(QuizServiceSOAP.class);   
    }

    private void initializeQuizService() {
       
        quizService.initialize(quizData);
    }

    @Test
    public void testQuizCorrectAnswer() {

        assertEquals(true, quizService.answer(question, answer));
    }

    @Test
    public void testQuizWrongAnswer() {

        assertEquals(false, quizService.answer(question, "anAnswer"));
    }
}

The publishing of the SOAP-based web service in the unit test is simple and fetching the service after publishing it is possible in a leightweight manner. Publishing the web service first and using later the browser or an external web service test tool based on the retrieval of the WSDL is also an opportunity.

REST-part unit test using the endpoint publisher:

@RunWith(value = Parameterized.class)public class QuizRESTServiceEndToEndTest {
   
    private static final String XPATH_QUIZ_URI_PART = "quiz";
    private static final String XPATH_QUIZ_RESPONSE = "/quiz:response";
    private static final String GET_REQUEST = "GET";
    private static final String POST_REQUEST = "POST";

    private static final String QUIZ_PORT = "quizPort";
    private static final String QUIZ_SERVICE = "quizService";
    private static final String QUIZ_URI = "urn:quiz";

    private static final String PUBLISH_URL = "http://127.0.0.1:8080/quiz";
   
    private final String XML_START = "<quiz:request xmlns:quiz ='urn:quiz'>";
    private final String XML_END = "</quiz:request>";

    private static final String FALSE_RESPONSE = "false";
    private static final String TRUE_RESPONSE = "true";
    private static final String QUESTION_DELIMITER = ":";
    private static final String QUESTION_ANSWER_DELIMITER = ";";
   
    private String[][] quizData;
    private final String question;
    private final String answer;
   
    private Dispatch<Source> dispatch;
    private String uri;

    public QuizRESTServiceEndToEndTest(final String[] questions,
                                                             final String[] answers) {

        this.question = questions[0];
        this.answer = answers[0];

        initializeQuizData(questions, answers);
    }

    private void initializeQuizData(final String[] questions,
                                               final String[] answers) {
       
        quizData = new String[1][2];
        quizData[0][0] = questions[0];
        quizData[0][1] = answers[0];
    }

    @Parameters
    public static Collection<String[][]> getTestParameters() {

        return Arrays.asList(new String[][][] {

                {{"firstQuestion"},{"firstAnswer"}},
                {{"secondQuestion"},{"secondAnswer"}}});
    }

    @Before
    public void setUp() throws URISyntaxException {

        publishQuizService();       
        lookupQuizService();   
   
        initializeQuizService();
    }

    private void publishQuizService() {
       
        Endpoint.publish(PUBLISH_URL, new QuizServiceRest());
    }

    private void lookupQuizService() throws URISyntaxException {
       
        uri = new URI(QUIZ_URI).toString();
           
        final QName serviceName = new QName(QUIZ_SERVICE, uri);
        final QName port = new QName(QUIZ_PORT, uri);
        final String endpoint = PUBLISH_URL;       
       
        final Service service = Service.create(serviceName);
        service.addPort(port,HTTPBinding.HTTP_BINDING, endpoint);
       
        dispatch = service.createDispatch(port,Source.class,Service.Mode.PAYLOAD);               
    }
   
    private void initializeQuizService() {
       
        invokePost(dispatch, buildPayload(convertQuizDataToString(quizData)));
    }
   
    private String invokePost(final Dispatch<Source> dispatch,                       
                                         final String payload) {
       
        final Map<String, Object> requestContext = dispatch.getRequestContext();
        requestContext.put(MessageContext.HTTP_REQUEST_METHOD, POST_REQUEST);       
       
        final StreamSource source = makeStreamSource(payload.toString());
        final Source result = dispatch.invoke(source);
       
        return parseResponse(result);
    }
   
    private StreamSource makeStreamSource(final String payload) {
       
        final ByteArrayInputStream stream = new ByteArrayInputStream(payload.getBytes());
        return new StreamSource(stream);
    }
       
    private String buildPayload(final String payload) {
       
        return XML_START + payload + XML_END;
    }
   
    private String convertQuizDataToString(final String[][] payload) {
       
        final StringBuilder builder = new StringBuilder();
       
        for(int i = 0; i < payload.length; i++) {
           
            builder.append(payload[i][0]);
            builder.append(QUESTION_DELIMITER);
            builder.append(payload[i][1]);
            builder.append(QUESTION_ANSWER_DELIMITER);
        }
       
        return builder.toString();
    }
   
    private String parseResponse(final Source response) {
                                   
        final DOMResult domResult = new DOMResult();
        String result = null;
        try {
           
            final Transformer transformer = TransformerFactory.newInstance().newTransformer();
            transformer.transform(response, domResult);
           
            final XPathFactory xpFactory = XPathFactory.newInstance();
            final XPath xp = xpFactory.newXPath();
           
            xp.setNamespaceContext(new NSResolver(XPATH_QUIZ_URI_PART, QUIZ_URI));
           
            result = xp.evaluate(XPATH_QUIZ_RESPONSE, domResult.getNode());                               
        }
        catch (TransformerConfigurationException ex) {
           
            throw new IllegalStateException(ex);
        }
        catch (TransformerFactoryConfigurationError ex) {
           
            throw new IllegalStateException(ex);
        }
        catch (TransformerException ex) {
           
            throw new IllegalStateException(ex);
        }
        catch (XPathExpressionException ex) {
           
            throw new IllegalStateException(ex);
        }
       
        return result;
    }
   
    @Test
    public void testQuizCorrectAnswer() {

        assertEquals(TRUE_RESPONSE,
                            invokeGet(dispatch,question + QUESTION_DELIMITER + answer));
    }

    @Test
    public void testQuizWrongAnswer() {

        assertEquals(FALSE_RESPONSE,
                           invokeGet(dispatch,question + QUESTION_DELIMITER + "anAnswer"));
    }
   
    private String invokeGet(final Dispatch<Source> dispatch,                           
                                       final String queryString) {
       
        final Map<String, Object> requestContext = dispatch.getRequestContext();
        requestContext.put(MessageContext.HTTP_REQUEST_METHOD, GET_REQUEST);
        requestContext.put(MessageContext.QUERY_STRING, queryString);
       
        final Source result = dispatch.invoke(null);
       
        return parseResponse(result);
    }
}

The RESTful web service unit test uses the endpoint publisher and the dispatch interface. The server side RESTful web services implements the provider interface. It is obviously more coding effort in the given scenario to publish the quiz backend model as RESTful web service in the unit test and the service implementation. The SOAP-part is in line with the quiz backend model, simply using the JAX-WS annotations. For the RESTful part it's (slightly) a little bit to much coding necessary. Praise for Java EE 6 which gives us JAX-RS, to reduce the coding effort of RESTful web services noticeable.


Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.

Freitag, 17. Juni 2011

Let it flow...

The Object Modeling Technique (OMT) introduced by James Rumbaugh defines for the analysis phase (OOA) three modeling types. Starting at the object level, defining an object model, the dynamic modeling is following for the core parts of the business logic mainly to visualize state changes. Object models and dynamic models covered through state diagrams are present in the UML today. The functional modeling, also known as data flow models, are not part of the UML. The reason might be the functional background of data flows, not recognized as an important modeling technique in the object-oriented world.

For my computer science degree I designed with OMT and developed a multimedia system in the programming language C which is a portable solution for Microsoft Windows NT and IBM OS/2. I designed  in the object-oriented style and used the functional programming language C to implement the model, that was possible with some tricks and effort. I remember one important point during modeling the multimedia system. I had some problems with the object model relationships and sometimes problems to define the dynamic model in detail. The data flow modeling was easier for me, more natural and rather fluent. 

Data Flow Diagram
At that time I thought the problem is my weak experience in modeling complex object-oriented systems. Later in my career I learned object-oriented modeling starting with the domain layer (DDD). I learned to visualize the software model in class and component diagrams using stereotypes and the power of UML diagrams. This power is in some cases weak when it touches the implementation and the differences in the model and the implementation occur. Continuous rework for the improvement of the models are necessary to avoid this weakness. Modeling on an appropriate level with appropriate details is not simple and some lessons are necessary to learn modeling on the right level. My advice is to try the Java EE architect certification as an opportunity to find this level.

During last weeks, I followed the discussion about Event Based Components (EBC) which is a technique to develop software flow-oriented. Ralf Westphal defined principles and patterns for the flow-oriented programming. I read some of his blog articles and listened to the discussions in the clean code developer forum. It seems to me that the .NET community is very eager in the field of flow-oriented programming. Later, I found a website from a developer whose aim is to bring EBC to the Java platform. Impressed of the flow-oriented modeling which again seems natural and fluent, usable on different levels and in line with the implementation, I was inspired to find a Flow Pattern

Therefore it was obvious to start Eclipse to do some TDD practice to dive into the flow. I admit that my knowledge about flow-oriented programming is weak and I rather think in object-oriented structures. This is what I have learned over the years developing software on the Java platform. Usually it is a worse starting position developing software in a field with weak domain knowledge, but it’s still a training  session. Starting with this background programming flows leading to a solution for a simple pattern for flow-oriented programming. Do not expect the "killer pattern" for a wide range of scenarios and problem solutions covered by this flow pattern.

The pattern is following the principle of the “input-process-output” model. It is usable in isolation or in chained scenarios. The use case for the pattern is a simple calculator board adding and subtracting values encapsulated in assemblies. The following discusses the flow pattern that is first documented with UML.

Flow Pattern

The “FlowChain” is an inspiration of the ChainOfResponsibility pattern. The ChainOfResponsibility pattern loosely couples assemblies and let the assemblies decide whether to process a value or not. The core of the pattern is based on Template Method by overriding the flow method to chain assemblies. The benefit of this pattern is to chain in the overriden flow method which is well encapsulated. The drawback is that you can only test assemblies covering their relationships, chained in the flow method. It’s not possible to test assemblies in isolation, that might be a drawback for some use cases.

The interface of the flow pattern is simple by calling the process method of an assembly instance which extends the flow pattern. The process method expects an input pin and returns an output pin as the result of the processing task. It’s not possible to build the chaining outside of the assemblies which is flexible but on the other side a slightly more complex programming interface.

The “FlowWire” is weaker coupled than the “FlowChain” with the possibility for outside wiring and testing of assemblies in isolation. In the flow pattern implementation the "FlowWire" extends the “FlowChain” with a wire method for outside wiring. The pattern consists of small super classes as the base for boards or assemblies of the boards using pins to fulfill the requirements of the “input-process-output” model.

The flow assemblies are based on classes whose instances are chained. It’s also possible to see flows as a chain of method calls which is finally more fine grained. The flow pattern covers simple use cases, not supporting complex routings and broadcasts.

FlowChain (stronger coupling)

public abstract class FlowChain<T> {
               
    public FlowPin<T> process(final FlowPin<T> inPin) {

        return flow(inPin);
    }
   
    protected abstract FlowPin<T> flow(final FlowPin<T> inPin);
}

 FlowWire (weaker coupling)

public abstract class FlowWire<T> extends FlowChain<T> {
   
    public FlowPin<T> wire(final FlowWire<T> assembly,
                                      final FlowPin<T> inPin) {
               
        return assembly.process(inPin);
    }
}

FlowPin

public class FlowPin<T> {

    private final T value;
   
    public FlowPin() {
       
        this(null);
    }
   
    public FlowPin(final T value) {
       
        this.value = value;
    }
   
    public T value() {
       
        return value;
    }

    @Override
    public int hashCode() {

        final int prime = 31;
        int result = 1;
        result = prime * result + ((value == null) ? 0 : value.hashCode());
       
        return result;
    }

    @Override
    public boolean equals(Object obj) {
       
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;              
        final FlowPin<T> other = (FlowPin<T>) obj;
        if (value == null) {
            if (other.value != null)
                return false;
        } else if (!value.equals(other.value))
            return false;
       
        return true;
    }
}

FlowChainTest

import static org.junit.Assert.assertEquals;
import org.ccd.flow.pattern.api.FlowChain;
import org.ccd.flow.pattern.api.FlowPin;
import org.junit.Test;

public class FlowChainTest {

    @Test
    public void testSubtractAssembly() {
      
        final FlowChain<Integer> subtractAssembly = new SubtractAssembly();      
        assertEquals(new FlowPin<Integer>(7), subtractAssembly.process(new FlowPin<Integer>(12)));
    }
  
    @Test
    public void testAddAssembly() {
      
        final FlowChain<Integer> addAssembly = new AddAssembly();      
        assertEquals(new FlowPin<Integer>(17), addAssembly.process(new FlowPin<Integer>(12)));
    }
  
    @Test
    public void testCalculatorBoard() {
      
        final FlowChain<Integer> calculatorBoard = new CalculatorBoard();      
        assertEquals(new FlowPin<Integer>(7), calculatorBoard.process(new FlowPin<Integer>(2)));
    }
  
    private class CalculatorBoard extends FlowChain<Integer> {

        @Override
        protected FlowPin<Integer> flow(final FlowPin<Integer> inPin) {
              
            final AddAssembly add = new AddAssembly();
          
            return add.process(inPin);
        }
    }
  
    private class AddAssembly extends FlowChain<Integer> {

        @Override
        protected FlowPin<Integer> flow(final FlowPin<Integer> inPin) {
              
            final Integer value = addValue(inPin.value());
            final SubtractAssembly subtract = new SubtractAssembly();                      
                      
            return subtract.process(new FlowPin<Integer>(value));
        }

        private Integer addValue(final Integer value) {
          
            return value + 10;
        }
    }
  
    private class SubtractAssembly extends FlowChain<Integer> {

        @Override
        protected FlowPin<Integer> flow(final FlowPin<Integer> inPin) {
              
            Integer value = substractValue(inPin.value());          
              
            return new FlowPin<Integer>(value);
        }
      
        private Integer substractValue(final Integer value) {
                              
            return value - 5;
        }
    }
}

FlowWireTest

import static org.junit.Assert.assertEquals;
import org.ccd.flow.pattern.api.FlowPin;
import org.ccd.flow.pattern.api.FlowWire;
import org.junit.Test;

public class FlowWireTest {

    @Test
    public void testSubtractAssembly() {
      
        final FlowWire<Integer> subtractAssembly = new SubtractAssembly();      
        assertEquals(new FlowPin<Integer>(7), subtractAssembly.process(new FlowPin<Integer>(12)));
    }
  
    @Test
    public void testAddAssembly() {
      
        final FlowWire<Integer> addAssembly = new AddAssembly();      
        assertEquals(new FlowPin<Integer>(22), addAssembly.process(new FlowPin<Integer>(12)));
    }
  
    @Test
    public void testCalculatorBoard() {
      
        final FlowWire<Integer> calculatorBoard = new CalculatorBoard();      
        assertEquals(new FlowPin<Integer>(7), calculatorBoard.process(new FlowPin<Integer>(2)));
    }
  
    private class CalculatorBoard extends FlowWire<Integer> {

        @Override
        protected FlowPin<Integer> flow(final FlowPin<Integer> inPin) {
          
            final FlowWire<Integer> add = new AddAssembly();  
            final FlowPin<Integer> outPin = wire(add,inPin);
          
            final FlowWire<Integer> subtract = new SubtractAssembly();          
            return add.wire(subtract,outPin);
        }
    }
  
    private class AddAssembly extends FlowWire<Integer> {

        @Override
        protected FlowPin<Integer> flow(final FlowPin<Integer> inPin) {
              
            final Integer value = addValue(inPin.value());
                                                  
            return new FlowPin<Integer>(value);
        }

        private Integer addValue(final Integer value) {

            return value + 10;
        }
    }
  
    private class SubtractAssembly extends FlowWire<Integer> {

        @Override
        protected FlowPin<Integer> flow(final FlowPin<Integer> inPin) {
              
            final Integer value = substractValue(inPin.value());          
              
            return new FlowPin<Integer>(value);
        }
      
        private Integer substractValue(final Integer value) {

            return value - 5;
        }
    }
}

The flow pattern is developed in an object-oriented manner to support the programming of flows. Their is usually a border between object-oriented and flow-oriented programming. But listen: "This border is in your mind". You see it's possible to mix different styles to find a solution. The Flow Pattern is object-oriented and the implemented flows in the test cases are a mixture of the flow-oriented and object-oriented style.Using the power of both styles is appropriate to define a solution that fullfils the criteria of "input-process-output", encapsulation, isolation, loose coupling (coupling happens only on the board) and high cohesion to develop small assemblies and wire them to build a bigger assembly named as board. Wire boards and build a bigger solution that I call the flow-oriented application.You see, let it flow...to cross the border.


Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.

Montag, 13. Juni 2011

Code of Conduct

There is no doubt that Robert C. Martin is an experienced programmer who gives the community of programmers a framework to write clean code. Robert published a lot of articles about principles to improve software quality which flows in his book “Clean Code: A Handbook of Agile Software Craftsmanship”. This book is rather technique centric, viewing code smells, guidelines how to refactor smells, explaining best practices for writing clean code and testing. The mission of the book is to sharpen programmer skills and learn how to reflect the daily written code using the boy scout rule for continuous improvements.

In Germany, Ralf Westphal and Stefan Lieser founded the clean code developer community launching a website which summarizes the principles and practices of clean code. In technical discussion the community understands the principles of clean code knowing that there must be more than technical terms. Proposals were made to missing principles, new ideas came up and the constructive critique boosted the understanding of clean code principles. Discussions about object-oriented principles, testing, agile models, programming language specifics followed along with the discussions in the clean code community. Code katas, to show how to write clean code, were used to view the benefits of clean code for discussion in the community. There is still some critique regarding code bloat, doubts about some principles in detail and how to handle the development lifecycle (e.g. TDD "yes or no" or how and when to do the design for modeling the software).

The Clean Coder
One fact was completely missing: The attitude of a professional programmer. First, I was doubtful about the new book of Robert which defines a code of conduct for professional programmers describing the attitude of a professional in detail. Personally, I don’t like books which advice behavior, how to handle schedules, conflicts and pressure. On the other hand, I like Robert’s style of writing books, explaining daily work scenarios and true experience of his craftsmanship career. The introduction of the book includes the excerpt: "I’ am a programmer, too. I’ve been a programmer for 42 years (don’t panic); and in that time - let me tell you – I’ve seen it all…". Robert has definitively a strong voice which is based on his experience leading to his brilliant reputation. But no one is born which such a voice, it’s still hard work to get every day a little bit better following the principles of clean code.

His message is to work clean (QA should find nothing), to have a work ethic, to know your field and domain, to learn continuously, to practice frequently, to collaborate with colleagues and to mentor junior developers as well as identification with your company and customers to mention the primarily subjects. Diving deeper into the subjects explains when to say “yes” or “no”, how to say it and what it really means. Some subjects are already known in the agile community like the “definition of done” (Scrum) and the commitment (the contract for agreement) to deliver features.

The book is easy to read and full of real life experience of a programmer with brilliant reputation. I have also experience working in different teams using different models and programming languages (mainly C/C++ and Java) for software development. I found a lot of experience written in this book which I have also made in my career. Leading to the simple principle to work clean, to avoid high pressure, conflicts and overtime. In Robert's words the best technical programmer could be fired when he has no professional attitude and his work is like a mess. We all know that pressure, conflicts and overtime could lead to bad code quality. But all this reasons are not acceptable as an excuse for bad code quality. Therefore, avoid pressure by clear communication and commitments which are reachable. Work clean to avoid overtime, and if overtime is necessary, the overtime should happen only in a defined time frame. Resolve conflicts like a professional and when this is not possible resolve it with a sense of humor (laugh may help). It’s also natural to have a wall in front of you, sitting on the keyboard and nothing happens. You may solve this situation by asking for a pair partner or simply accept the situation trying to resolve by going to the window to get some fresh air.

Interesting for me is Robert's opinion according the Flow Zone. For an experienced programmer the Zone is usually like a vacuum, working in a closed room with high speed producing large pieces of source code. I compare the Zone with a mountain bike trail having the power to cycle without hurts forgetting all sorrows in a perfect manner. It’s a nice feeling which could on the other hand be exhausting, if you cycle too far away from your home, it could be a problem to get back. Robert has the same opinion. Avoid the Zone although you write more code with higher speed. But the danger is to lose the big picture not getting the way back home. His advice is to enter the Zone only during practicing a training kata.

I could write even more about the book: “The Clean Coder – A Code of Conduct for Professional Programmers" but I might leave it up to you to participate in a valuable publication which completes the craftsmanship series with useful hints to work clean in the software development lifecycle with the appropriate attitude and responsibility to get things done.

Samstag, 11. Juni 2011

Vertical separation

Die vertikale Trennung schreibt die Definition von Variablen und Methoden nahe bei ihrer Verwendung vor. Verbunden mit der vertikalen Trennung ist die Kohäsion von Methoden. Eine kohäsive Methode ist klein und erfüllt genau eine Aufgabe. Das Band zwischen der vertikalen Trennung und kohäsiven Methoden wird durch die Lesefolge gebunden. Ausgehend von einer öffentlichen Methode werden die privaten Methoden, welche in der öffentlichen Methode verwendet werden, unterhalb der öffentlichen Methode im Quellcode geschrieben. Der vertikale Abstand zwischen den kohäsiven Methoden soll gering sein.

Die vertikale Trennung wird oft mit dem Lesen einer Tageszeitung gleichgesetzt. In einer Tageszeitung werden bewusst hervorgehobene Überschriften (Headlines) genutzt, denen ein Artikel folgt. Das Single Level of Abstraction (SLA) Prinzip gibt vor, dass in Klassen und Methoden möglichst nur ein Abstraktionsniveau verwendet werden soll. Teil dieses Prinzips ist, dass vorzugsweise mit lokalen Variablen in Methoden anstelle von Attributen einer Klasse gearbeiten wird. Die Nutzung lokaler Variablen vermeidet Seiteneffekte und beugt Problemen, die durch Nebenläufigkeit entstehen können vor. Quellcode, der dem SLA Prinzip folgt, ist gut zu lesen und zu verstehen. Der Leser erkennt durch die saubere Anordnung des Quellcodes sehr viel leichter essentielle Abschnitte ohne sich dabei in Details zu verfangen. Die vertikale Trennung und Bildung kleiner kohäsiver Methoden ist eine essentielle Praktik bei der Refaktorisierung durch Anwendung der Pfadfinderregel.

Die vertikale Trennung erhöht aber nicht nur die Lesbarkeit von Quellcode, sondern ist partiell nötig, um Kompilierprobleme zu Gunsten eines klaren Methodendesigns zu beseitigen.

Beispiel „Wildcard Capture“:

public static void reverse(List<?> list);
public static <T> void reverse(List<T> list);

Die Methodensignatur der “reverse” Methode kann auf Basis eines Wildcards oder mit einem Typparameter beschrieben werden. Die Wildcard Methodensignatur ist kürzer und lesbarer, bringt aber die Problematik mit sich, dass die übergebene Liste per Definition nicht mit neuen Elementen erweitert werden darf. Ein Kompilierfehler zeigt dieses Problem an. Das Problem ist durch eine zusätzliche Methode, die der vertikalen Trennung genügt, lösbar.

Quellcode einer einfachen ListHelper-Klasse:
 
public class ListHelper {

    public static void reverse(final List<?> list) {
       
        assertNotNull(list);       
        assertNotEmpty(list);
       
        reverseOperation(list);
    }
   
    private static void assertNotNull(final List<?> list) {
       
        if(null == list) {
           
            throw new NullPointerException("List argument is null!");
        }
    }

    private static void assertNotEmpty(final List<?> list) {
       
        if(list.isEmpty()) {
           
            throw new IllegalArgumentException("List contains no elements!");
        }
    }
   
    private static <T> void reverseOperation(final List<T> list) {
       
        final List<T> tmpList = new ArrayList<T>(list);
        final int maxPositions = list.size() - 1;
        int readPosition = 0;
       
        for(int writePosition = 0; writePosition <= maxPositions; writePosition++) {
           
            readPosition = maxPositions - writePosition;
            list.set(writePosition, tmpList.get((readPosition)));
        }
    }
}

In der Methode "reverseOperation" hat der Typparameter den Wildcard erfasst (captured).

Testfälle der ListHelper-Klasse:

@RunWith(value=Parameterized.class)
public class TestListHelper {
   
    private final List<String> original;
    private final List<String> reversed;
   
    public TestListHelper(final String[] original,
                                   final String[] reversed) {
       
        this.original = Arrays.asList(original);
        this.reversed = Arrays.asList(reversed);
    }
   
    @Parameters
    public static Collection<String[][]> getTestParameters() {
       
        return Arrays.asList(new String[][][]{
               
                {{"one", "two", "three"},{"three", "two", "one"}},
                {{"1", "2", "3"},{"3", "2", "1"}}});
    }
   
    @Test
    public void testReverse() {
                    
        ListHelper.reverse(original);       
        assertEquals(reversed, original);
    }
}

public class TestListHelperExceptions {

    @Test(expected=NullPointerException.class)
    public void testReverseWithNullPointerException() {
                    
        ListHelper.reverse(null);
    }
   
    @Test(expected=IllegalArgumentException.class)
    public void testReverseWithIllegalArgumentException() {
                    
        ListHelper.reverse(new ArrayList<String>());
    }   
}


Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.

Freitag, 27. Mai 2011

Shy coding

Schüchterner Quellcode minimiert die Abhängigkeiten zwischen Objekten. Die Reduzierung der Abhängigkeiten führt zu loser Kopplung, die wiederum isolierte testbare Softwarebausteine hervorbringt. Testbare Softwarebausteine mit ihren spezifischen Eigenschaften (gekapselt, kohäsiv, Mock freundlich, etc.), fördern die Evolvierbarkeit einer Software-Applikation.

Das „Law of Demeter (LoD)" definiert Regeln für schüchternen Quellcode. Das klassische Law of Demeter basiert auf einer Veröffentlichung von Karl Lieberherr und Ian Holland. Das Law of Demeter wird  mit dem "Principle of Least Knowledge" gleichgesetzt. Dieses Prinzip sagt aus, dass Objekte nur mit eng befreundeten Objekten sprechen sollen. Die Interaktionen zwischen Objekten sollen durch Einhaltung des Prinzips reduziert werden, sodass lose gekoppelte Software-Architekturen entstehen. Wird das Prinzip häufig verletzt, entstehen zerbrechliche Systeme, die schwer zu warten und zu verstehen sind.

Anfällig für die Verletzung des Prinzips sind Facaden, die eine einfachere Sicht auf ein komplexes Teilsystem bieten und Aggregatsobjekte (Kompositionen, Kontextobjekte, etc.), die eine Menge an Funktionalitäten bündeln. Vorsicht ist auch bei Mediatoren geboten, die komplexere Interaktionen zwischen Objekten vereinfachen sollen.

Entwicklungsumgebungen wie Eclipse unterstützen die Suche von Objekten durch ausgefeilte Kontextfunktionen. Dennoch ist es ratsam Interaktionen zwischen Objekten zu minimieren, sodass eine Suche nach Objekten nicht nötig ist. Die transitive Navigation führt dabei besonders zu unübersichtlichen Objektvernetzungen, die nur schwer aufgelöst werden können.

Beispiel für eine transitive Navigation:

long count = order.getProduct().getItems().countItems();

Besser ist:

long count = order.countItems();

Beispiel für einen Verstoß gegen das Law of Demeter:

Constructor(final Context context) {


     this.view = context.getFrame().getView();
}

Korrekt ist:

Constructor(final View view) {


     this.view = view;
}

Merkregeln, rufe nur Methoden auf von...
  1. dir selbst (normalerweise private Methoden),
  2. Objektreferenzen, die als Methodenargument übergeben wurden,
  3. jedem selbst erzeugten Objekt (in Methoden der Klasse erzeugten Objekten),
  4. in Assoziation stehenden Klassen (Attribute der Klasse)

Sonntag, 15. Mai 2011

Testing for Clean Agility

Softwareentwicklungsmethoden nutzen Tests zur Verifikation und Sicherstellung der Qualität einer Softwareanwendung. Im klassischen Wasserfallmodell erfolgt der Testzyklus nach der Implementierungsphase in Form einer Verifikation der Implementierung. Wurde die Verifikation erfolgreich abgeschlossen, erfolgt das Deployment der Applikation in die Kundenumgebung. In der Kundenumgebung finden im Rahmen der Abnahmeprozesse die Akzeptanztests statt.

Das Wasserfallmodell funktioniert sofern der Kunde zuvor eine Detailspezifikation abgenommen hat und keine größeren Änderungen im Projektverlauf fordert. Häufig ist sich der Kunde im Vorfeld eines Projektes aber unschlüssig und kennt noch nicht alle seine Anforderungen im Detail. Das Wasserfallmodell blockiert das Lernen des Kunden und das Finden der geeigneten Anforderungen im Projektverlauf.

Im Wasserfallmodell sind die einzelnen Phasen strikt voneinander getrennt, sodass das Design und die Akzeptanztests weit auseinander liegen. Die Distanz zwischen der Implementierungsphase und der Verifikation der Software durch Testzyklen im Anschluss an die Implementierung ist darüber hinaus zu weitläufig.

Wasserfallmodell

Agile Methoden wie Scrum nutzen überschaubare Planungs- und Implementierungsphasen von max. 30 Tagen. Tests stehen bei den agilen Methoden im Vordergrund. Agile Softwareentwicklung nutzt kurze Entwicklungszyklen und zeitnahes Feedback. Das zeitnahe Feedback wird durch automatisierte Testläufe und die kontinuierliche Integration erreicht.

Automatisierte Testläufe, die zyklisch ausgeführt werden, sind eine wesentliche Voraussetzung für Refaktorisierungen zur Verbesserung der Softwarequalität. Die kontinuierliche  Verbesserung der Softwarequalität ist fest in den agilen Methoden  verankert. In Scrum ist normalerweise kein eigener Raum für Refaktorisierungen vorhanden. Refaktorisierungen und die Fehlerbehebung werden im Sprint bearbeitet. Nur in Ausnahmefällen werden größere Refaktorisierungen in ein technisches Backlog übernommen und in die Sprint-Planung miteinbezogen.

Laut der „Definition of Done“ erstellen Scrum Teams lieferbare User Stories in hoher Qualität. Fehlende Qualitätssicherung und mangelnde Refaktorisierungen würden sich in Sprints in der Folge negativ auf die Velocity eines Scrum Teams auswirken. Bei CCD sind Refaktorisierungen und automatisierte Tests ebenfalls tief verwurzelt. Die Pfadfinderregel propagiert geradezu Refaktorisierungen und damit die Beseitigung von „Code Smells“.

Im agilen Lager gibt es dennoch unterschiedliche Meinungen wann und wie Testzyklen durchgeführt werden sollen. Es ist kein Streitpunkt mehr, dass automatisierte Tests zur Erzielung hoher Softwarequalität notwendig sind. Das schnelle Feedback und die mit den automatisierten Tests einhergehenden Vorteile sind ebenfalls unstrittig und anerkannt. Testbare Softwarebausteine sind in der Regel durch die lose Kopplung isoliert von ihrer Umgebung, weisen eine hohe Kohäsion (SRP = Single Responsibility Principle) auf und sind gut gekapselt (IHP = Information Hiding Principle). Die Nutzung von Testattrappen (Mocks) erlaubt es unabhängig von den externen Abhängigkeiten einer komplexen Softwarearchitektur testbare Softwarebausteine zu erstellen.Dies sind alles Vorteile, welche Tests attraktiv machen.

Rückt der Test First und Test Driven Design/Development (TDD) Ansatz die Unit-Tests zunächst in den Mittelpunkt verfolgen andere Ansätze, wie beispielsweise DDT (Design Driven Testing), das Ziel, ausgehend von einem stabilen Design, testbaren Quellcode zu erstellen. Bei diesem Ansatz sind Implementierungsregeln einzuhalten, damit testbare Softwarebausteine entstehen können.

Beispiele für DDT Implementierungsregeln:
  1. Verwende die Vererbung mit Bedacht (FCoI = Favour Composition over Inheritance)
  2. Beachte das Single Level of Abstraction (SLA) Prinzip
  3. Vermeide Singletons, statische Initialisierungen, statische Methoden und Variablen
  4. Nutze die lose Kopplung
  5. Trenne Oberflächen- und Geschäftslogik
  6. Entferne Initialisierungen aus dem Konstruktor
Testbare Softwarebausteine werden bei DDT aus den Anwendungsfällen der Analysephase abgeleitet und entsprechen den Akzeptanztests. DDT geht Top-Down von der Benutzersicht aus. TDD hingegen ist eher als Bottom-Up Methode anzusehen, bei dem der Verwender eines Softwarebausteins eine zentrale Rolle spielt. Der Verwender ist allerdings nicht unbedingt der Benutzer des Softwarebausteins, sondern möglicherweise ein anderer Softwarebaustein.

Bei TDD entstehen sehr viele kleine Unit-Tests. Die Gegner von TDD argumentieren deshalb gerne, dass TDD zu viele  Testfälle hervorbringt, die nicht den gleichen Stellenwert haben wie die Kerntestfälle der Benutzersicht. Dennoch sind Unit-Tests für die Qualität von Software wesentlich, weil Unit-Tests grundsätzlich den Quellcode verbessern. Die Begründung für diese Aussage ist einfach: Ein Unit-Test ist ein Verwender des Quellcodes, sodass der Quellcode (insbesondere die Schnittstellen) im Zuge der einhergehenden Refaktorisierungen stetig besser wird. Übermäßiger Fleiß beim Schreiben der Unit-Tests kann allerdings zu viel des Guten sein. Eine hohe Testabdeckung durch Unit-Tests ist nämlich nicht immer ein Indikator für Softwarequalität. Code Coverage Tools, welche die Testabdeckung messen nutzen den Quellcode einer Applikation zur Analyse der Testabdeckung nicht aber alle möglichen Permutationen, die semantisches Wissen beinhalten.

Frei nach dem Motto „Design muss sein!“ gehen deshalb neuere Ansätze zur Entwicklung von Software davon aus, dass die Akzeptanztests an erster Stelle stehen und die Unit-Tests ausschließlich ihren Platz in der Implementierungsphase haben. Demzufolge steht an erster Stelle ein Design in unterschiedlich starker Ausprägung. Ein Design drückt sich durch die Modellierung von Softwarebausteinen und das Nachdenken über Anforderungen, Schnittstellen und Randbedingungen aus. In der Summe gute Voraussetzungen für Softwarequalität ohne einen großen Graben wie beim Wasserfallmodell überwinden zu müssen.

In TDD spricht man heute von einer inneren und äußeren Feedback Loop. Die äußere Feedback Loop wird durch einen Akzeptanztest (End-To-End Test) gebildet und die innere durch eine Reihe von Unit-Tests.

TDD (innere/äußere Feedback Loops)

Bei Scrum spricht man von "Acceptance Test Driven Development (ATDD)”. Bei diesem Ansatz werden die funktionalen Anforderungen einer User Story als automatisierte Tests vor der Implementierung der Funktionalität geschrieben. Fehlgeschlagene Tests bringt man bei der inkrementellen Umsetzung einer User Story zum Laufen. Diese Tests werden für gewöhnlich vom Product Owner durchgeführt, der ein starkes Interesse an dem Wert einer User Story aus Sicht des Geschäftsvorteils hat. Scrum schreibt einem Team nicht vor, wie es im Sprint zu arbeiten hat. Unit-Tests, Refaktorisierungen und CCD-Prinzipien sind dennoch essentiell für das Erreichen eines Sprint-Ziels und der „Definition of Done“ lieferbare User Stories zu erstellen.

ATDD-Kreislauf

Die bisher diskutierten Methoden nutzen Feedbacks und Testlevels. Die Einhaltung der Testlevels und häufiges zeitnahes Feedback haben sich in der Praxis bewährt und sind fest mit dem agilen Gedanken verbunden.

Testlevels beantworten folgende Fragen:
  1. Akzeptanztests: Funktioniert eine Kette von Softwarebausteinen und das Gesamtsystem?
  2. Integrationstests: Funktioniert der entwickelte Quellcode im Kontext externer Schnittstellen und Systeme, die wir nicht ändern können?
  3. Unit-Tests: Erledigen die Objekte den Job richtig und sind die Objektschnittstellen einfach zu verwenden?
Im Java-Umfeld sind für jeden Testlevel einfach zu nutzende Werkzeuge und Frameworks zur automatisierten Testdurchführung vorhanden. Für Akzeptanztests können Fitnesse, Selenium und JSFUnit verwendet werden. Integrationstests lassen sich mit Buildsystemen (Continuous Integration) wie Maven, Ant und Continuous Integration Servern wie Hudson, CruiseControl, Apache Gump sowie Apache Continuum durchführen. Unit-Tests sind die Domäne von JUnit, dem populären Framework für automatisierte Entwicklertests.

Eine „Best Practice“ ist es, ausgehend von den Anforderungen, Akzeptanztests zu definieren/auszuführen, in der Implementierungsphase Unit-Tests konform dem TDD Mantra einzusetzen und zyklisch automatisierte Integrationstests beim „Nightly Build“ durch ein Continuous Integration (CI) System durchführen zu lassen. Durch diese "Best Practice" können Applikationen sehr viel schneller und in höherer Qualität entwickelt und regelmäßig mit geringer Reibung ausgeliefert werden. Wie wichtig automatisierte Tests und die kontinuierliche Integration für die agile Bewegung sind, verdeutlicht die Definition der „Balanced Agility“ von Mike Beedle. Diese Definition setzt minimale Voraussetzungen für den agilen Entwicklungsprozess voraus.

Definition für "Balanced Agility":
  1. Scrum als Vorgehensmodell
  2. Grundlegende Engineering Practices
    • Kontinuierliche Integration
    • Automatisierte Testdurchführung auf unterschiedlichen Testlevels
    • Release Management
Die Definition von Mike Beedle zu adaptieren und um die CCD-Prinzipien zu erweitern ergibt das Synonym „Clean Agility".