Testing the Class Purpose

We have separated the testing of the singleton nature from the testing of the class’s functional purpose and addressed how to test the singleton nature. Testing the functional purpose is easy, right? It would be, except for one thing: we cannot construct an instance except through the singleton mechanism. The difficulty in testing the singleton’s purpose is that we have no way to test it with a Fresh Fixture. The more tests we write, the higher the chances we will create Erratic Tests. So what are our options?

Briefly, our choices are to

• Remove the singleton nature

• Make no changes and live with testing a shared fixture

• Relax the constraints on instance

• Or relax the constraints on construction

Let’s take a look at each of these options in turn.

The often least considered option is to simply change the design to eliminate the use of the Singleton pattern. Unless there is a significant performance motivation, you are creating a publicly visible API in which the control is important, or you are establishing an association with a single physical resource, you may not really need a singleton. (See the sidebar “A Word on Difficult-to-Test Designs”). Most of the time it is sufficient for the application to only create one instance and to support that uniqueness by convention and other mechanisms. However, sometimes singleton is the design of choice.

Living with the shared fixture may be viable for a short period of time, but it does not plan strongly for the growth of your test base. That leaves us with the options to relax the singleton constraints.

In general, relaxing the singleton constraints opens up usage possibilities contrary to the singleton intent. We are deliberately making the tradeoff between singleton enforcement and testability. We should select the alternatives that make the least egregious breaks in encapsulation and solve the most problems with testing singletons.

Relaxing the Constraints on instance

A commonly applied solution is to create a setter for the instance with restricted access. A typical implementation for the singleton coded previously might look like Listing 10-3.

Listing 10-3: Relaxing the access on instance for testability

protected synchronized
Singleton setInstance(Singleton newInstance) {
  Singleton oldInstance = instance;
  instance = newInstance;
  return oldInstance;
}

The method is synchronized to ensure consistency against the nonatomic test and set in getInstance(). Unlike a canonical setter, the method returns the old instance value. Because this method is purely in support of testing, returning the old value allows the test to reset it when finished. The use of protected explicitly specifies an access level that allows other package members to use it. We could also use the package default access level, but many code cleanliness regimens do not accept that for good reasons.

So we have created a mechanism that allows us to inject our own instance of the class, right? But wait. How do we create that instance? The constructor is private, so we cannot create one directly. The class is declared final, so we cannot derive from it. Either of these relaxations brings us to the relaxation of construction anyway, and the initial premise adds overhead to that.

We still haven’t really solved our underlying issue, which is that of the shared fixture. We have added the ability to swap out the fixture and to restore it. That does not force any particular test to swap the fixture, guarantee that it will restore it, or prevent it from changing the fixture that it restores. And even though we have synchronized the fixture swap, that does not ensure transactionality with respect to the complete running of the tests, so threaded test runners will encounter race conditions.

Additionally, it creates the code smell Test Logic in Production [xTP]. We have added a method to our production code that is purely in support of testing without compelling benefit or protection.

This is not a compelling option.

Relaxing the Constraints on Construction

Opening up access to the constructor allows us to create fresh fixtures and to completely test the class’s purpose independently from its singleton nature in a way that also supports concurrent test execution. The question then becomes: How should we accomplish this?

The general answer is: In the way that leaves us least exposed. The details vary depending on the facilities available in the language we are using. The tools we apply are those described in Chapter 9. In Listing 10-1, there are two primary options.

Using only simple changes to the code, we can change the access specifier on the constructor from private to either default (by removing it) or protected. The choice between them depends on your tolerance for the implicit default access scope. There is no need to make it public. If we put our unit tests into the same package as the class under test, then we can access the constructor to create a fresh fixture for each test as in Listing 10-4.

Listing 10-4: Testing our singleton with relaxed access to the constructor

@Test
public void testDoSomething() {
  Singleton sut = new Singleton();
  sut.doSomething();
  // Make assertions about the effects of doSomething()
}

An alternative uses reflection to coerce access to the private constructor. The code for this can be cumbersome and is probably best encapsulated in a helper method, fixture-creation method, or implicit setup method. All of these have the side effect of making the code more difficult to read and understand, but it may be worth it to preserve the singleton encapsulation.

You can use the access-coercion techniques from Chapter 9 to rewrite your access to the private constructor with the reflection-based coercion. This can work well if all developers running your tests use a sufficiently uniform development environment, but it will raise barriers to participation for code bases such as open-source projects.

C++ provides a more declarative alternative using the friend keyword. If a class declares its corresponding test class as a friend, that test class has access to all private members. It does not provide the fine-grained control exhibited by either access-level promotion or access coercion, but it preserves encapsulation better than the former and leaves the choice to the implementation better than the latter. Simply adding the line

friend class TestSingleton;

within the Singleton class will give the TestSingleton test class full access to its internals. You could use it a little more surgically by declaring each test method as a friend function, but that would be harder to maintain over time.

The applicability of Perl’s declarative scope changing varies depending on which Perl object approach you are using. Assuming you are using a Perl OO approach that supports private construction,5 it can often be bypassed with code like that in Listing 10-5.

5. One of the more comprehensive Perl OO packages, Damian Conway’s Class::Std, does not explicitly support private constructors.

Listing 10-5: Invoking the singleton constructor directly using package redeclaration

package TestSingleton;
...
sub testDoSomething {
  my sut;
  {
    package Singleton;
    sut = Singleton->new();
  }
  ...
}

The Perl package name is almost universally the class name in Perl OO approaches. Assuming a convention of prepending Test to the package name for the tests, this code creates a subordinate code block in which it redeclares the package within that block, thus making Perl think that the call to the constructor is coming from within the class. If you are not used to Perl, you may have a slight sick feeling in your stomach right now, but we all make tradeoffs for testability.

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

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