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?
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
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.