A Simple Example That Makes You Think

Let’s look at an example in Java. Listing 2-1 shows a simple Java class with several clues about its intent in the way it is constructed. Consider the ScoreWatcher class that is part of a system to track sports scores. It encapsulates the function of obtaining the scores from a news feed.

Listing 2-1: A simple Java class to demonstrate intentional construction

class ScoreWatcher {
  private final NewsFeed feed;
  private int pollFrequency; // Seconds
  public ScoreWatcher(NewsFeed feed, int pollFrequency) {
    this.feed = feed;
    this.pollFrequency = pollFrequency;
  }
  public void setPollFrequency(int pollFrequency) {
    this.pollFrequency = pollFrequency;
  }
  public int getPollFrequency() {
    return pollFrequency;
  }
  public NewsFeed getFeed() {
    return feed;
  }
  ...
}

First, look at the attributes defined for the class. The class author defined the feed attribute as final3 while pollFrequency is not. What does this tell us? It expresses the intent that feed should only be set once on construction, but pollFrequency can be modified throughout the life of the object. The presence of both a getter and setter for pollFrequency but only a getter for feed reinforces that reading.

3. In Java, the final keyword indicates that the declared variable cannot be changed once it has been initialized.

But that only gives us an understanding of the implementation intent. What functional intent might this support? We could make a reasonable conclusion in the context that exactly one of these classes should be allocated for each news feed we could use. We could also infer that perhaps there is exactly one NewsFeed per score to be monitored, which is used to initialize the ScoreWatcher. We could also speculate that the management of multiple feeds, if available, might hide behind the single feed interface. This would require verification but seems reasonable under the circumstances.

However, that hypothesis has a weakness that is probably due to a limit in the expressiveness of Java. Without knowing the construction of the NewsFeed class, we can speculate that it is possible to modify the object referenced by feed even though the reference itself cannot be changed. In C++, we could declare the attribute as

const NewsFeed * const feed;

which communicates not only that the pointer cannot be changed, but that you cannot use the pointer to change the object it references. This provides an additional marker of contextual immutability in C++ that does not exist in Java. It is relatively easy in Java to make all instances of a class immutable. It takes considerably more effort to make an object immutable through a particular reference, perhaps requiring the creation of an immutability proxy to wrap the instance.

So how does this change our tests? The construction of the class—the implementation—clearly specifies that the feed given to the class should not change during the lifetime of the class. Is this the intent? Let’s look at a test that verifies that assumption in Listing 2-2.

Listing 2-2: A test that verifies that the news feed does not change in Listing 2-1

class TestScoreWatcher {
  @Test
  public void testScoreWatcher_SameFeed() {
    // Set up
    NewsFeed expectedFeed = new NewsFeed();
    int expectedPollFrequency = 70;

    // Execute SUT4
    ScoreWatcher sut = new ScoreWatcher(expectedFeed,
      expectedPollFrequency);

    // Verify results
    Assert.assertNotNull(sut); // Guard assertion
    Assert.assertSame(expectedFeed, sut.getFeed());

    // Garbage collected tear down
  }
}

4. SUT is an acronym for Software Under Test. [xTP]

In JUnit, the assertSame assertion verifies that the expected and actual references refer to the same object. Going back to our speculation about the intent of the class, it is reasonable to assume that it is important to reference the same feed, but is the same NewsFeed an overspecification of that condition? For example, what if the implementation chose to enforce the immutability of the initial news feed by cloning the attribute before returning it from the getter, thus ensuring that any changes did not affect the internal state of the ScoreWatcher’s NewsFeed? In such a case, testing for the identity of the constructor argument is incorrect. The design intent more likely requires verifying the deep equality of the feed.

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

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