Verification by Injection

All but the outermost components of our system—the leaf components—rely on other components to accomplish their tasks. These other components are called collaborators, or Depended-On Components (DOCs) in Meszaros’s terminology [xTP]. Whether writing unit tests that test only the value added or when testing an integrated system, you often need to create substitutes for collaborators, known as test doubles. Especially at the unit level, injection is the most common way to introduce test doubles.

Generally speaking, dependency injection is the software-design pattern in which collaborators are inserted (i.e., injected) at runtime to satisfy the needed functionality. When testing, we can leverage various forms of dependency injection to insert mocks or stubs or simply to sever a heavyweight, time-consuming, or otherwise inconvenient dependency.

Let’s look at a simple example of testing by dependency injection. We will examine a wide variety of techniques for dependency injection later in Chapter 12. Imagine we are writing a system to manage a cluster of compute servers. Part of our allocation algorithm depends on the CPU load of the server. The code to determine whether a remote CPU is still within our allocation threshold might look like Listing 6-17.

Listing 6-17: Code to determine whether a remote CPU is within a certain threshold

class CPUManager {
  private final HostConnection connection;
  private int warningThreshold; // Percent

  public CPUManager(HostConnection connection,
      int warningThreshold) {
    this.connection = connection;
    this.warningThreshold = warningThreshold;
  }

  public boolean isHostUnderThreshold()
      throws RemoteException {
    // May throw RemoteException
    int load = connection.getCPULoad();
    return load < warningThreshold;
  }
}

Obviously, this class would have more methods than this—at least a setter and two getters and likely much more functionality—but this is sufficient to illustrate basic dependency injection. The declaration that HostConnection.getCPULoad() might throw RemoteException tells Java developers that it encapsulates a network connection. If we want to unit test this class, a network connection—not to mention the assumption of the machine it might connect to on the other end and trying to control the CPU load—would be very inconvenient.

But what if we could forego the network connection altogether and take control of the return from getCPULoad()? After all, it is not part of the value added by the isHostUnderThreshold() method. It is merely a source of data.

We can create our own version of HostConnection to test all the threshold variations, as shown in Listing 6-18.

Listing 6-18: A custom HostConnection to inject threshold variations

public class HostConnectionStub implements HostConnection {
  private final int cpuLoad;

  public HostConnectionStub(int cpuLoad) {
    this.cpuLoad = cpuLoad;
  }

  @Override
  public int getCPULoad() {
    return cpuLoad;
  }
}

While creating a test stub class might be overkill for a single variation, you can imagine testing for CPU loads below, at, or above the threshold as well as tests for zero load and potentially tests for values beyond 100% in the case of multiple cores per CPU. For several tests, this is a useful class. Let’s inject it into a test (Listing 6-19).

Listing 6-19: Using a custom implementation to inject data values

public void testIsHostUnderThreshold_Over() {
  int threshold = 50;
  int load = threshold + 20;
  HostConnection connection = new HostConnectionStub(load);
  CPUManager sut = new CPUManager(connection, threshold);

  boolean result = sut.isHostUnderThreshold();

  assertFalse(result);
}

With a few extra lines of code, we have constructed a stub that satisfies our functional requirements and avoids a network connection and reliance on external hardware. By supplying that to the constructor, we have injected it into the software under test, simplifying a whole category of tests for data variations. We will show ways to inject errors and other behaviors later, in Chapters 9 and 12.

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

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