Use Existing No-Op Classes

In my opinion, one of the most underutilized of the common design patterns is the Null Object pattern.3 The concept is simple. Instead of returning the ubiquitous null (or its various equivalents such as undef, nil, or NULL) and testing for it as a special value, we return an object of the expected type that implements the behavior associated with an unknown, missing, or indistinct value. Several such objects may reasonably exist for a given situation, giving rise to Martin Fowler’s Special Case pattern4 as a generalization.

3. Surprisingly to many, this is not one of the original Gang of Four design patterns but was first proposed in the Pattern Languages of Program Design series. Martin Fowler [REF], Josh Kerievsky [RTP], and Robert Martin [CC08] all use it significantly.

4. http://martinfowler.com/eaaCatalog/specialCase.html

Null object implementations are particularly useful in testing a stub. Take, for example, some code that passes an interface or abstract class as a parameter. What if the Connection used in Listing 8-7 were an interface instead of a class? To override even a single method we would have to implement all of the methods. In some languages, we could use a mocking framework like JMock, EasyMock, or Mockito in Java. Let’s examine a technique we can use in almost all languages.

One can easily imagine NetRetriever as part of an overall application that could perhaps manage multiple connections. And this application might have the concept of a current connection, at least for the purposes of its user interface. How might one implement the current connection before an actual connection is chosen or even defined? Rather than use null and test for that special value everywhere as in Listing 8-10, let’s create a NoConnection class that automatically initializes the current connection. This class would look something like Listing 8-11.

Listing 8-10: An example of using null values to indicate error conditions

if (currentConnection == null) {
  throw new InvalidConnectionException();
}
currentConnection.doSomething();

Listing 8-11: An example Null Object pattern implementation of a Connection

class NoConnection implements Connection {
  @Override
  public void open() throws RemoteException {
    throw new InvalidConnectionException();
  }

  @Override
  public void close() {
    // Who cares if we close a nonexistent connection?
    return;
  }

  @Override
  public void doSomething() throws RemoteException {
    throw new InvalidConnectionException();
  }
  ...
}

Now the conditional test for currentConnection’s null-ness can be removed. The NoConnection class behaves as one would expect a lack of connection to behave. In fact, in our example, it would behave much as a misbehaving connection might. Not only would this simplify our overall application code, it would also reduce the possibility of the dreaded NullPointerException that plagues many applications.

But there is an additional benefit for our purposes in writing tests. We now have a complete implementation of the interface as the basis for our injected objects. Using this null object implementation, the test from Listing 8-9 would use NoConnection as a stub (Listing 8-12).

Listing 8-12: Testing using the implementation from Listing 8-11

@Test(expected = RetrievalException.class)
public void testRetrieveResponseFor_Exception()
    throws RetrievalException {
  Connection connection = new NoConnection() {
    @Override
    public void open() throws RemoteException {
      throw new RemoteException();
    }
  };

  NetRetriever sut = new NetRetriever(connection);

  sut.retrieveResponseFor(null);
}

In fact, if InvalidConnectionException is derived from RemoteException as you would surmise from the throws clause in the method signature, we would not even need to override the open() method, simplifying our test to that in Listing 8-13. You could also derive it from RuntimeException, but should only do so if throwing a RuntimeException is a valid expectation in the context. Nothing in this context indicates that it is anticipated.

Listing 8-13: Simplified test relying on our null object implementation

@Test(expected = RetrievalException.class)
public void testRetrieveResponseFor_Exception()
    throws RetrievalException {
  Connection connection = new NoConnection();

  NetRetriever sut = new NetRetriever(connection);

  sut.retrieveResponseFor(null);
}

While this is a powerful technique, we should also be aware that it introduces coupling between our test and an additional production class. However, in the case of null object implementations, that coupling tends to be of little consequence.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.165.70