Jay Fields is a software developer at DRW Trading. He has a passion for discovering and maturing innovative solutions. His most recent work has been in the Domain Specific Language space where he's delivered applications that empowered subject matter experts to write the business rules of the applications. He is also very interested in maturing software design through software testing. Jay is a DZone MVB and is not an employee of DZone and has posted 116 posts at DZone. You can read more from them at their website. View Full User Profile

Method Chain with Snippet

06.25.2009
| 4013 views |
  • submit to reddit
I've noticed a pattern pop up a few times in my Java code in the past 6 months. Maybe it's a decent pattern, or maybe I only have a hammer.

The problem I'm trying to solve is setting some global state, running a test, and ensuring that the global state is set back to it's original (or correct) value. My usual solution to this problem is to remove global state, but not all global states are created equally. The two global states I've been unsuccessful at removing are the current time and system properties.

In my previous post I described how I freeze time using the Joda library. The example code for freezing time was the first time I used a Method Chain with a Snippet. At the time I thought it was an ugly solution, but the best I could come up with.

A few months later I was testing some code that set and read from the system properties. My first tests set the properties and didn't clean up after themselves. This quickly caused trouble, and I found myself turning to Method Chain with Snippet again.

Here's some example code where I verify that setupDir doesn't overwrite a default property:
    @Test
public void shouldNotOverrideDir() {
new Temporarily().setProperty("a.dir", "was.preset").when(new Snippet() {{
new Main().setupDir();
assertEquals("was.preset", System.getProperty("a.dir"));
}});
}

And, here's the code for the Temporarily class
    public class Temporarily {
private Map<string, string=""> properties = new HashMap<string, string="">();

public void when(Snippet snippet) {
for (Map.Entry<string, string=""> entry : properties.entrySet()) {
System.setProperty(entry.getKey(), entr</string,></string,></string,><string, string=""><string, string=""><string, string="">y.getValue());
}
}

public Temporarily setProperty(String propertyName, String propertyValue) {
if (System.getProperty(propertyName) != null) {
properties.put(propertyName, System.getProperty(propertyName));
}
System.setProperty(propertyName, propertyValue);
return this;
}
}


The code works by setting the desired state for the test, chaining the state cleanup method, and passing the test code as a Snippet to the state cleanup method. The code exploits the fact that Java will execute the first method, then the argument to the chained method, then the chained method.

For the previous example, the 'setProperty' method is executed, then the Snippet is constructed and the initializer is immediately executed, then the 'when' method is executed. The Snippet argument isn't used within the when method; therefore, no state needs to be captured in the Snippet's initializer.

This pattern seems to work well whenever you need to set some state before and after a test runs. However, as I previously mentioned, it's much better if you can simply remove the state dependency from your test.

From http://blog.jayfields.com/

0
Your rating: None
Published at DZone with permission of Jay Fields, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Sebastian Mueller replied on Thu, 2009/06/25 - 3:52am

This sure looks like a hammer to me. This code is pretty much over designed to look like a fluent interface but  does not do its job very well, IMHO.

If your "Snippet" throws an exception the properties will never be restored.  If "Snippet" was a runnable that was only executed at "run()-time" the "when" method could have invoked the runnable and do so within a try finally block, first setting the properites and then restoring them in the finally after the execution of the "snippet". This would have made a little sense at least to me. Because then it would have been possible to actually reuse the "Temporarily" instance and not depend on less obvious-side effects that depend on the execution order.Also the state of the system properties woud have only been modified if the during the execution of "when". Now if you omit the when() the properties will be set some value and will never be reset, either.

Just my 2 cents.

sub online replied on Fri, 2009/06/26 - 1:29am

You think I was born yesterday.links of londonCalm down! Don't get so excited. Calm down. Keep your pants shirt on! What's the hurry? Keep your pants shirt on.links of londonIt's a long story.You're overdoing it.What does it matter!Tiffany JewelleryPerfectionist.You'll regret it if you don't go.Better than nothing.When's class gonna end!I'll know when the time comes.That's a bad idea if I've ever heard one.All things must come to an end.

Mladen Girazovski replied on Fri, 2009/06/26 - 6:05am

Why not use Mocks?

andy darlow replied on Sat, 2009/06/27 - 3:14am

i also believe that using Mock objects is the way forward.

The problem that you're experiencing is the age old design problem of singletons. It's made worse when static methods are used to represent singletons.

To solve the problem, I'd recommend creating a class to abstract the functionality of your static methods and then inject an instance of that class into your class (ok, its a siingleton - but at least you can now create an instance of it). .The class is effectively a delegate for the static methods. Once you've done that then you can create mock alternatives to the class and inject them into your class for testing.

Using a class to abstract the behavour normally aids readability as well. In your example, a class called, say, FileSystem (or what ever) whould help the reader understand what the property a,dir is all about. Currently. looking at the code, I don't know what a.dir is.

Hope that this helps

If you need examples then just say amd I'll post.

Cheers

Andy

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.