Exact Verification by Value

Before we proceed further, let’s apply a technique from Chapter 6 to our current topic of dealing with strings. One of the basic techniques was sharing constants. In our example, we showed how to use a constant for a default value in a way that simultaneously clarified the intent of the code and made it more testable. At the risk of stating the obvious, that same technique applies well to string verification—and not just verification of default values.

Let’s look at the example of a web controller that, for reasons of compatibility, needs to translate URL parameters from an older version to a newer one. For brevity, let’s assume that we only need to translate a single parameter and that we have a method in our class that deals with parameter translation (see Listing 7-6).

Listing 7-6: Parameter translation in a web controller

public class Controller {
  ...
  public String translateParameterName(String name) {
    if ("oldname".equals(name)) {
      return "newname";
    }
    return name;
  }
}

This is a simple transformational method whose outputs are strictly predictable from its inputs: an ideally testable method. Two tests for it are shown in Listing 7-7.

Listing 7-7: Two tests for the code in Listing 7-6

@Test
public void testTranslateParameterName_NewParam() {
  String expectedParam = "notoldname";
  Controller sut = new Controller();

  String actualParam =
      sut.translateParameterName(expectedParam);

  assertEquals(expectedParam, actualParam);
}

@Test
public void testTranslateParameterName_OldParam() {
  String inputParam = "oldname";
  Controller sut = new Controller();

  String actualParam = sut.translateParameterName(inputParam);

  assertEquals("newname", actualParam);
}

While these tests are intent revealing, they are not very resilient to change. For one, the first test makes the assumption that expectedParam is not the value that will be translated. We could remedy that with an assertion or assumption, but what would we compare it to? On the other hand, the second method suffers considerably from coincidental equality. Both the values of the input and the expected parameters are literal strings that (hopefully!) happen to correlate with the values in the implementation class.

With a quick bit of refactoring, we can improve the testability of translateParameterName() as shown in Listing 7-8.

Listing 7-8: Refactoring Listing 7-6 for better testability

public class Controller {
  public static final String OLD_PARAMETER_NAME = "oldname";
  public static final String NEW_PARAMETER_NAME = "newname";
  ...
  public String translateParameterName(String name) {
    if (OLD_PARAMETER_NAME.equals(name)) {
      return NEW_PARAMETER_NAME;
    }
    return name;
  }
}

That was simple, wasn’t it? Two simple Extract Constant [REF] refactorings give us significantly improved testability. The strings in question were not private values in any sense. They were as public as possible when included in the URL for the request. Making them public constants of the class does nothing to break encapsulation. Now we can rewrite the tests to take advantage of the new constants (Listing 7-9).

Listing 7-9: Refactoring the tests from Listing 7-7 to take advantage of our testability improvements

@Test
public void testTranslateParameterName_NewParam() {
  String expectedParam = "notoldname";
  assertThat(expectedParam,
    not(equalTo(Controller.OLD_PARAMETER_NAME)));
  Controller sut = new Controller();

  String actualParam =
      sut.translateParameterName(expectedParam);

  assertThat(actualParam, equalTo(expectedParam));
}

@Test
public void testTranslateParameterName_OldParam() {
  String inputParam = Controller.OLD_PARAMETER_NAME;
  Controller sut = new Controller();

  String actualParam = sut.translateParameterName(inputParam);

  assertThat(actualParam,
      equalTo(Controller.NEW_PARAMETER_NAME));
}

By the addition of a precondition guard assertion2 and by changing three literal strings to constant references we have added the ability to verify a precondition of one test and made both tests resistant to changes in the values being used. Now let’s look at how far we can take this technique.

2. We are using the Hamcrest matchers for a more literate assertion style.

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

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