Replace Collaborators

A collaborator is a component your software interacts with. For our purposes, we care about the components that the component under test uses to accomplish its goal. Let’s look at a reworked version of the previous NetRetriever example (Listing 8-8).

In a more realistic example, the connection would be managed separately from the NetRetriever. In addition to supporting the principle of separation of concerns, it abstracts the connection in a way that allows it to be reused and passed in from a larger context. While managing the open and close of the connection may not be the most neighborly or sporting approach in that design context, let’s tolerate it for now.

Listing 8-8: Listing 8-6 rewritten to pass the Connection to the constructor

public class NetRetriever {
  private Connection connection;

  public NetRetriever(Connection connection) {
    this.connection = connection;
  }

  public Response retrieveResponseFor(Request request)
      throws RetrievalException {
    try {
      connection.open();
      return makeRequest(connection, request);
    } catch(RemoteException re) {
      logError("Error making request", re);
      throw new RetrievalException(re);
    } finally {
      if (connection.isOpen()) {
        connection.close();
      }
    }
  }
}

Now we pass a Connection to the constructor when we create a NetRetriever object. The makeRequest() method uses that Connection to make the request. The connection-management code in retrieveResponseFor() manages the connection more directly. Let’s change our testing strategy to inject an error via the Connection instance rather than the makeRequest() method used in the previous section (Listing 8-9).

Listing 8-9: Injecting an exception by replacing the Connection collaborator.

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

  NetRetriever sut = new NetRetriever(connection);

  sut.retrieveResponseFor(null);
}

Rather than override a method of the NetRetriever class, we have overridden a method of a collaborator and used that to inject an error into the method under test. In this instance, conventional design principles of encapsulation and separation of concerns have increased our testability rather than hindered it. The presence of an additional object to support the functionality of the class under test makes it easier to inject various influences—in this case, an error condition—into the flow of execution.

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

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