Saturday, November 12, 2011

Testing singletons while avoiding their constructors

Singletons are evil, and here's why.

Fair enough. Yet they exist, and they're not intrinsically evil--just misused. Can we mock enough to make testing them feasible? Yep, and here's a Contrived Example™ that shows how (and why we might want to).

"Embedded" singletons or utility classes can make testing is problematic. Injected singletons are different; then it's an issue of whether or not it should be a singleton at all--different discussion.

"Embedded" singletons look like this. (Utility classes are essentially the same, minus an instance.)

public class UsesLongRunningCtor {
/** Retrieves data and returns processed result. */
public String transformData() {
// LongRunningCtor "embedded"; direct reference, like a static utility class.
String rawData = LongRunningCtor.instance().fetch();
return rawData.replace("Raw", "Processed");
}
}
view raw 00_misuse.java hosted with ❤ by GitHub

Let's say (a) we need to test this code, (b) we cannot modify this code, and (c) the singleton's constructor is long-running, but required for fetch() execution. For testing, then, we need to (a) avoid the constructor for speed reasons, and (b) mock the fetch() method to return known data for the test. We can't just mock fetch(), because the constructor would still run.

Here's our (contrived) singleton class; we sleep() in the constructor to pretend it's doing something interesting like caching data from a web service, to be returned by fetch().

/** Pseudo-singleton with a slow constructor. Don't make singletons like this. */
public class LongRunningCtor {
private static final LongRunningCtor INSTANCE = new LongRunningCtor();
private LongRunningCtor() {
try { Thread.sleep(60 * 1000); }
catch (InterruptedException e) { e.printStackTrace(); }
}
public String fetch() { return "Raw data"; }
public static LongRunningCtor instance() { return INSTANCE; }
}

The undecorated test does what you'd expect, and takes as much time as you'd expect.

/** Slow test is slow. Calls undecorated singleton. */
@Test
public void testSlowSingleton() {
long start = System.currentTimeMillis();
UsesLongRunningCtor underTest = new UsesLongRunningCtor();
assertEquals("Processed data", underTest.transformData());
long end = System.currentTimeMillis();
out.println("Unmocked test time ~" + (end - start) / 1000.0 + " seconds.");
}
// Output is ~ "Unmocked test time ~60.0 seconds."

Now, with a combination of PowerMock and EasyMock, we'll put the kibosh on that, eliminate the constructor, and return the data we want. (PowerMock sits on top of EasyMock or Mockito. (Both are great, although I tend towards Mockito.)

@RunWith(PowerMockRunner.class)
@PrepareForTest(LongRunningCtor.class)
public class TestWithoutCtor {
@Test
public void testSlowSingleton() {
long start = System.currentTimeMillis();
// Suppress ctor *before* createPartialMock and static mock, or ctor is run.
suppress(constructor(LongRunningCtor.class));
// Could also do mockStaticPartial(LongRunningCtor.class, "instance").
mockStatic(LongRunningCtor.class);
// Could also do createPartialMock(LongRunningCtor.class, "fetch").
LongRunningCtor mock = createMock(LongRunningCtor.class);
expect(LongRunningCtor.instance()).andReturn(mock);
expect(mock.fetch()).andReturn("Processed data");
replayAll(mock);
UsesLongRunningCtor underTest = new UsesLongRunningCtor();
assertEquals("Processed data", underTest.transformData());
long end = System.currentTimeMillis();
verify(mock);
out.println("Unmocked test time ~" + (end - start) / 1000.0 + " seconds.");
}
}
view raw 04_mockoff.java hosted with ❤ by GitHub

Most of the test class is self-explanatory. The nutshell version is that we annotate the test class itself and tell it to run with the PowerMock runner (@RunWith), and that we're going to be messing with LongRunningCtor's innards (@PrepareForTest).

Inside the test itself, we tell it to suppress LongRunningCtor's constructor (before mocking, otherwise the constructor will fire during the createPartialMock() call). We also prepare for mayhem by calling mockStatic(). (This mocks all the class's static methods; we could also choose specific static methods to mock using mockStaticPartial().)

Our test now takes a fraction of the time because we're skipping the slow constructor, and our mock returns known data so we can exercise only the calling code.

Ideally, code is structured so this kind of byte-code treachery is unnecessary--it's a great reason for dependency injection/inversion of control. In the real world, technical and timing constraints don't always allow the kind of restructuring we'd like.

With the aid of some tools that do the low-level dirty work for us, we have a relatively clean way to work around some types of system limitations, and still write tests that can execute quickly.

The gist used in this post also includes the relevant Maven dependencies.

Thursday, November 10, 2011

A trip to the Rake-track.

(Because it's like "racetrack", and it makes Rake tasks faster, and... oh, never mind.)

Tired of waiting for Ruby to spin up just so you can run a "routes" command, or your latest "db:migrate"?

Use rake-sh and start up a rake console for running tasks without the initial spin up. It'll take a "rake routes" from four seconds down to under a second. Rake task completion? Naturally. Use "t" for tasks, "! " (note there's a space after the bang) to run a shell command.

If you get a message about an "uninitialized constant Rake::DSL" when you run rake-sh, no sweat: add require 'rake/dsl_definition' to the top of your Rakefile.

Breaking flow sucks for developers: a few seconds doesn't sound like much. In actual time, it isn't much--but it's the distractions that kill, the times-between-doing. Keep them to a minimum by fully exploiting your tools.