Elvis carried away by spaceships

I love teaching Groovy to existing Java developers, because they have such a hard time holding back Tears Of Joy when they see how much easier life can be. Today, though, I did a quick demo that resulted in a line of Groovy that was so amusing I had to post it here.

Consider a trivial POGO (Plain Old Groovy Object) called Course:

class Course
    String name
    int days
    String toString() { "($name,$days)" }
}

The goal was to take a collection of courses and sort it by the number of days. That’s really easy in Groovy:

def courses = [
    new Course(name:'Groovy',days:4),
    new Course(name:'Grails',days:3),
    new Course(name:'Spring',days:4),
    new Course(name:'Hibernate',days:3)
]

assert courses.toString() == '[(Groovy,4), (Grails,3), (Spring,4), (Hibernate,3)]'

courses.sort { it.days }

assert courses*.days == [3, 3, 4, 4]

The sort method in the java.util.Collection class is part of the Groovy JDK, meaning it’s one of the methods Groovy adds to the standard Java libraries. It takes a closure of either one or two arguments. In this case, I’m using the one-argument closure, which is used to select a property on which to base the sort. By specifying it.days in the closure, I’m telling the sort method to sort the courses based on their days property. Then I verify that the sort worked by checking that the courses are the right order, using the spread-dot operator to just look at the number of days.

In class the question that always comes up is, can I sort by days and then by name? In other words, if two courses have the same number of days, can I then sort by the name property?

That’s what the two-argument closure on the sort method is for. The two arguments are references to any pair of courses, and the closure should return a negative number, zero, or positive number according to whether the first course is less than, equal to, or greater than the second.

Here’s where things get amusing. The sort I want is:

courses.sort { a,b ->
    a.days <=> b.days ?: a.name <=> b.name
}

assert courses.toString() == '[(Grails,3), (Hibernate,3), (Groovy,4), (Spring,4)]'

The body of the closure on sort uses the spaceship operator <=>, which returns -1, 0, or 1 depending on whether the left side is less than, equal to, or greater than the right side. I use spaceship to compare the days properties. Then I add the Elvis operator ?: which means if the days comparison is not zero, use it, but otherwise use the following comparison, which uses another spaceship to compare by name.

It’s only after writing the code in class that one of the students pointed out that I had Elvis in between two spaceships, leading to the following observations:

  1. The spaceships are there to return Elvis to his home planet
  2. It takes two spaceships, working in tandem, to carry Elvis away, in much the same way two swallows can carry a coconut in tandem
  3. Therefore, the Elvis being carried away must be the fat Elvis from the 70s, rather than the thin, cool Elvis from the 50s

Either way, after the sort is finished, Elvis has left the building.

Thank you, thank you very much.

Groovy StubFor magic

I finished revising the testing chapter in Making Java Groovy (the MEAP should be updated this week), but before I leave it entirely, I want to mention a Groovy capability that is both cool and easy to use. Cool isn’t the right word, actually. I have to say that even after years of working with Groovy, what I’m about to describe still feels like magic.

Here’s the issue: I have a class that uses one of Google’s web services, and I want to test my class even when I’m not online. That means I need to mock the dependency, which isn’t all that hard. The problem is that there’s no explicit way to get my mock object into my own service. Yet, with Groovy’s MockFor and StubFor classes, I can mock a dependency, even when it’s instantiated as a local variable inside my class.

Let me show you the code. I’ll start with a simple POGO called Stadium:

class Stadium {
    String street
    String city
    String state
    double latitude
    double longitude
    
    String toString() {
        "($street,$city,$state,$latitude,$longitude)"
    }
}

I use this in my Groovy Baseball application, which accesses MLB box scores online and displays the daily results on a Google Map. The Stadium class holds location data for an individual baseball stadium. When I use it, I set the street, city, and state and have the service compute latitude and longitude for me.

My Geocoder class is based on Google’s restful geocoding service.

class Geocoder {
    String base = 'http://maps.google.com/maps/api/geocode/xml?'
   
    void fillInLatLng(Stadium stadium) {
        String urlEncodedAddress =
                [stadium.street, stadium.city, stadium.state].collect {
                    URLEncoder.encode(it,'UTF-8')
                }.join(',+')
        String url = base + [sensor:false, address:urlEncodedAddress].collect { it }.join('&')
        def response = new XmlSlurper().parse(url)
        String latitude = response.result.geometry.location.lat[0] ?: "0.0"
        String longitude = response.result.geometry.location.lng[0] ?: "0.0"
        stadium.latitude = latitude.toDouble()
        stadium.longitude = longitude.toDouble()
    }
}

First I take the stadium’s street, city, and state and add them to a list. Then the collect method is used to apply a closure to each element of the list, returning the transformed list. The closure runs each value through Java’s URLEncoder. In looking at the Google geocoder example, I see that they separate the encoded street from the city and the city from the state using “,+“, so I do the same using the join method.

The Google geocoding service requires a parameter called sensor, which is true if the request is coming from a GPS-enabled device and false otherwise. In the Groovy map, I set its value to false, and set the value of the address parameter to the string from the previous line. The collect method on the map converts each entry to “key=value“, so joining with an ampersand and appending to the base value gives me the complete URL for the stadium.

Most restful web services try to provide their data in a format requested by the user. The content negotiation is usually done through an “Accept” header in the HTTP request, but in this case Google does something different. They support only XML and JSON output data, and let the user select which one they want through separate URLs. The base URL in the service above ends in xml. Google lists the JSON version as preferred, but that’s no doubt because they expect the requests to come through their own JavaScript API. I’m making the request using Groovy, and the XmlSlurper class makes parsing the result trivial.

(Since Groovy 1.8, the JsonSlurper class also makes parsing JSON trivial, and I have a version that does that, too, but the difference really isn’t significant here.)

The resulting block of XML that is returned by the service is fairly elaborate, but as you can see from my code, the data is nested through the elements GeocodeResponse/result/geometry/location/lat and GeocodeResponse/result/geometry/location/lng. After invoking the parse method (which returns the root element, GeocodeResponse), I just walk the tree to get the values I want.

I do have to protect myself somewhat. The Google service isn’t nearly as deterministic as I would have expected. Sometimes I get multiple locations when I search, so I added the zero index to make sure I always get the first one. Also, some time during the last year Google decided to start throttling their service. I have a script that uses the Geocoder for all 30 MLB stadiums, and unfortunately it runs too fast (!) for the Google limits. I know this because I start getting back null results if I don’t artificially introduce a delay. That’s why I put in the Elvis operator with the value 0.0 if I don’t get a good answer.

So much for the service; now I need a test. If I know I’m going to be online, I can write a simple integration test as follows:

import static org.junit.Assert.*;
import org.junit.Test;

class GeocoderIntegrationTest {
    Geocoder geocoder = new Geocoder()
    
    @Test
    public void testFillInLatLng() {
        Stadium google = new Stadium(street:'1600 Ampitheatre Parkway',
            city:'Mountain View',state:'CA')
        geocoder.fillInLatLng(google)
        assertEquals(37.422, google.latitude, 0.01)
        assertEquals(-122.083, google.longitude, 0.01)
    }
}

I’m using Google headquarters, because that’s the example in the documentation. I invoke my Geocoder’s fillInLatLng method and check the results within a hundredth of a degree.

(The fact that the Google geocoder returns answers to seven decimal places is evidence that their developers have a sense of humor.)

Now, finally, after all that introduction, I reach the subject of this post. What happens if I’m not online? More to the point, how do I test the business logic in the fillInLatLng method without requiring access to Google?

What I need is a mock object, or, more precisely, a stub.

(A stub stands in for the external dependency, called a collaborator. A mock does the same, but also validates that the caller accesses the collaborator’s methods the right number of times in the right order. A stub helps test the caller, and a mock tests the interaction of the caller with the collaborator, sometimes called the protocol. Insert obligatory link to Martin Fowler’s “Mocks Aren’t Stubs” post here.)

In my Geocoder class, the Google service is accessed through the parse method of the XmlSlurper. Even worse (from a testing point of view), the slurper is instantiated as a local variable inside my fillInLatLng method. There’s no way to isolate the dependency and set it from outside, either as an argument to the method, a setter in the class, a constructor argument, or whatever.

That’s where Groovy’s StubFor (or MockFor) class comes in. Let me show it in action. First, I’ll set up the answer I want.

String xml = '''
    <root><result><geometry>
        <location>
            <lat>37.422</lat>
            <lng>-122.083</lng>
        </location>
    </geometry></result></root>'''
    
def correctRoot = new XmlSlurper().parseText(xml)

The xml variable has the right answer in it, nested appropriately. I use the XmlSlurper to parse the text and return the root of the tree I want.

Now I need a Stadium to update. I deliberately put in the wrong street, city, and state to make sure that I only get the right latitude and longitude if I insert the stub correctly.

Stadium wrongStadium = new Stadium(
    street:'1313 Mockingbird Lane',
    city:'Mockingbird Heights',state:'CA')

(Yes, that’s a pretty obscure reference. For details, see here. And yes, I’m old. If you’re in the right age group, though, you now have the show’s theme song going through your head. Sorry.)

The next step is to create the stub and set the expectations.

def stub = new StubFor(XmlSlurper)
stub.demand.parse { correctRoot }

The StubFor constructor takes a class as an argument and builds a stub around it. Then I use the demand property to say that when the parse method is called, my previously computed root is returned rather than actually going over the web and parsing anything.

To put the stub into play, invoke its use method, which takes a closure:

stub.use {
    geocoder.fillInLatLng(wrongStadium)
}

That’s all there is to it. By the magic of Groovy metaprogramming, when I invoke the parse method of the XmlSlurpereven when it’s instantiated as a local variable — inside the use block the stub steps in and returns the expected value.

For completeness, here’s the complete test class:

import static org.junit.Assert.*
import groovy.mock.interceptor.StubFor
import org.junit.Test

class GeocoderUnitTest {
    Geocoder geocoder = new Geocoder()
    
    @Test
    public void testFillInLatLng() {
        Stadium wrongStadium = new Stadium(
            street:'1313 Mockingbird Lane',
            city:'Mockingbird Heights',state:'CA')
        
        String xml = '''
        <root><result><geometry>
            <location>
                <lat>37.422</lat>
                <lng>-122.083</lng>
            </location>
        </geometry></result></root>'''
        
        def correctRoot = new XmlSlurper().parseText(xml)
        
        def stub = new StubFor(XmlSlurper)
        stub.demand.parse { correctRoot }
        
        stub.use {
            geocoder.fillInLatLng(wrongStadium)
        }
        assertEquals(37.422, wrongStadium.latitude, 0.01)
        assertEquals(-122.083, wrongStadium.longitude, 0.01)
    }
}

Since I’m only calling one method and only calling that method once, a stub is perfectly fine in this case. I could invoke the verify method if I wanted to (MockFor invokes verify automatically but StubFor doesn’t), but it doesn’t really add anything here.

There is one limitation to this technique, which only comes up when you’re doing the sort of Groovy/Java integration that is the subject of my book. You can only use StubFor or MockFor on a class written in Groovy.

All this code is available in the book’s Github repository at http://github.com/kousen/Making-Java-Groovy. This particular example is part of Chapter 5: Testing Java and Groovy.

As a final note, I should say that I dug into all of this, worked out all the details, and tried it in several examples. Then I finally did what I should have done all along, which was look it up in Groovy In Action. Of course, there it was laid out in detail over about five pages. Someday that will stop happening to me. 🙂

%d bloggers like this: