Sunday, December 29, 2013

Reducing complexity in Java using Groovy closures

While coding, do you ever get that feeling deep in your gut that the code you've just saved is good enough to get the job done, but you feel that it might have been over-engineered or probably is a bit too complex for the problem at hand.  That happened to me recently after saving some Java code I was using to test a web application.  I had just added another method (very similar to the half dozen other methods within the same class), when it occurred to me that if I continued down the path I had laid out for myself, the code could soon become too verbose.

Before delving into the code and the solution I came up with, there's a bit of context I'd like to share about the application under test.  The code in question revolves around filtering content displayed on certain web pages by entering a value.  The idea here, is to use Selenium to enter the specified value into the designated filter (Figure 1).

Figure 1. Example of web page displaying the filters

So, to handle this I came up with a number of methods that looked like
filterByTitle(enteredValue);
filterByStatus(enteredValue);
filterByReleaseDateOnFilm(enteredValue);

These methods send the enteredValue to the appropriate Selenium page object class for further processing -- i.e., sending the text value to the correct filter.  Each of the above methods were associated with the Films web page.  

However, another web page, labelled the Releases page, also displayed a release date filter.  This resulted in another release date filter method -- one specific to the Releases page.
filterByReleaseDateOnRelease(enteredValue);
As you can see, in order to differentiate between the two methods, I added an OnFilm and OnRelease suffix to each method, thereby identifying which web page each filter was associated with.  This solution was good enough to test the filters on each web page, however, it just did not feel right.

So, I started toying around with the idea of improving the code by implementing the functionality in Groovy. But how?  Each of the filter methods resembled the following Java code.  How could I improve on this kind of code?
private void filterByTitle(String enteredValue) {
        filmsPage.sendTitle(enteredValue);
}

private void filterByStatus(String enteredValue) {
        filmsPage.sendStatus(enteredValue);
}
Enter Groovy closures!

First, I identified what was common to each of the existing methods --  the enteredValue parameter.  Then, I asked myself what varied between each method -- it was the implementation (e.g., filmsPage.sendTitle(enteredValue)).  With this in mind I created the following method

void filterBy(String enteredValue, Closure closure) {
        closure.call(enteredValue)
}

This method takes 2 parameters -- the enteredValue and a closure.  The implementation of this method is designed to call the closure, passing it the enteredValue parameter.  Once, I had this in place, I replaced the existing methods with something like

filterBy(enteredValue) { value ->
 filmsPage.sendTitle(value)
}

filterBy(enteredValue) { value ->
 filmsPage.sendStatus(value)
}
The end result leads to less verbosity (in the way each method is named) and more maintainable code.

Tuesday, November 26, 2013

How about some grapes with that Groovy script

Recently, I created my first Groovy script. Mind you, I've been tinkering with Groovy for the past couple of weeks, but most of the stuff I've been doing has involved just a few lines of code. So, when it came time to create a program that reads in data from a CSV file and prints out each column and its associated value in a readable manner, I turned to Groovy.

After some googling, I came across the groovycsv library. The groovycsv library relies on OpenCSV and makes reading and writing to/from CSV files easy. Don't believe me, well, check out this example from the groovycsv site:
def csv = '''Name,Lastname
Mark,Andersson
Pete,Hansen'''

def data = parseCsv(csv)
for(line in data) {
    println "$line.Name $line.Lastname"
}
Yet, if I wanted to run this script "as is", it would fail to work because the groovycsv library is not a part of the class path or some such nonsense ;-).  Well, a closer look at the example provided by the groovycsv team suggests that the following 2 lines of code should be included with the script.
@Grab('com.xlson.groovycsv:groovycsv:1.0')
import static com.xlson.groovycsv.CsvParser.parseCsv
Wait ... what?!?!  What's this @Grab thing?  Well, after some further googling, it turns out that Groovy scripts support Maven dependencies.  These can be included using the @Grab annotation.  The syntax is quite similar to Maven's groupId, artifactId and version call outs -- the difference being each value is separated by a colon ( : ).

The first time the script is executed, the dependency is retrieved from Maven (at run-time) and then its made available to the script by supplying the correct import statement.  Kinda cool.  But, what if you've got a number of dependencies that are required?

Are you hungry for grapes?  Grape stands for Groovy's Advanced Package Engine.  It's the mechanism that provides a Groovy script with the capability to grab a Maven dependency.   With respect to the example code above, even after adding the groovycsv dependency, the script would fail due to a missing CSVReader class. The CSVReader class is code that is contained within the OpenCSV library.  Here's how to overcome this issue using Grape:

@Grapes(
        [@Grab('net.sf.opencsv:opencsv:2.3')
        , @Grab('com.xlson.groovycsv:groovycsv:1.0')]
)
import static com.xlson.groovycsv.CsvParser.parseCsv

That's it!  The next time the script is run, each of the noted dependencies will be grabbed from Maven and placed within ~/.groovy/grapes.  Now, all dependencies are satisfied.  Even better -- if you share this script with someone else or you run it on a different platform, Groovy should grab what you need.