Adjust Visibility

Many of the things we have learned about encapsulation of software designs do not account for testability. We are encouraged to make things private, to provide access through methods, and to wrap behaviors in extra levels of indirection. While most of these heuristics, designs, and patterns suggest best practices and provide useful abstractions, very few account for the means and mechanisms to test them thoroughly.

Take simple procedural decomposition within a class, for example. Breaking an interface method into logical, purposeful submethods involves the creation of private methods according to the guidelines of encapsulation. However, if these submethods are as logical and purposeful as they should be, then they are also excellent candidates for direct testing to simplify the validation of the whole method that calls them.

Direct testing requires visibility outside of the class or at least techniques for breaking the encapsulation of the class. In languages like Java, encapsulation can be broken, security configuration permitting, through reflection. In some of the more dynamic languages, weak encapsulation is a language feature or, as in Perl, encapsulation is more of an afterthought and can be broken more easily. Regardless, even the easiest of encapsulation-breaking techniques is uglier and more cumbersome than direct invocation.

We will examine several techniques for adjusting visibility in a later chapter. For now, let’s look at an example in which we simply relax the strength of the ideal encapsulation. Consider a conventional data export method in Java (Listing 6-16).

Listing 6-16: A hypothetical data export method

public DataExport exportData(Search parameters, Format cues) {
  RawData raw = retrieveData(parameters);
  PreparedData prepared = prepareData(raw);
  return formatData(prepared, cues);
}

private RawData retrieveData(Search parameters) {
  ...
}

private PreparedData prepareData(RawData raw) {
  ...
}

private DataExport formatData(PreparedData prepared,
    Format cues) {
  ...
}

The exportData() method reads cleanly. The flow explains itself in a logical progression. Each of the submethods is encapsulated in a way that hides the implementation details from the client code. In doing so, the tests, which are also client code, must potentially perform a lot of fixture set up to test all three steps of the export.

For those not well versed in Java, Java has the concept of a package. Packages provide not just an organizational structure and a namespace, but classes that share a package also have certain access advantages between each other beyond those of classes in different packages. Specifically, a method6 without an access modifier (i.e., no public, private, or protected) has “package default” visibility. Any class in the same package can access and invoke that method. An often overlooked property of the protected modifier is that it is also accessible by other classes in the package.

6. Or member variable, for that matter, but we don’t do direct variable access, right?

In the previous example, either removing the private access modifiers from the three submethods or changing them to protected7 would make those methods accessible from other classes in the package. Using protected would also facilitate the Encapsulate and Override strategy discussed earlier. In Java and many other languages, the same package can be used in different physical source trees, making it easy—and, in fact, standard practice—to segregate the tests but put them in the same package for ease of access.

7. Personally, I prefer to use protected. One of the things I like least about Java is that the omission of a syntactic marker, the access modifier, has functional ramifications. I would much prefer if, for example, the package keyword were overloaded to explicitly denote package access.

This simple change weakens the encapsulation of the class a little, primarily for other closely related classes, but immensely increases the testability of the class. I prefer to think of these adjustments to conventional practice as pragmatic engineering alterations in support of the increased quality from testing. Perhaps the next generation of languages will have testing support built in so that we do not have to make these compromises.

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

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