Mocking HTTPBuilder with Spock

Spock rules! I’ve been consistently impressed by its power and readability. After using it for a month or so I’ll never go back to writing plain ol’ JUnit tests.

I managed to do an awesome mock of HttpBuilder to test a Grails service (one which subscribes a user to a MailChimp marketing list).

Here’s the service I am testing. I inject a httpBuilderFactoryService so that it is easy to mock the HTTPBuilder.

class MarketingSubscriptionService {
    static final String MAILCHIMP_API = 'https://us7.api.mailchimp.com/2.0'
    def httpBuilderFactoryService
    def grailsApplication

    def subscribe(User user) throws IOException {
        if (grailsApplication.config.mailchimp?.enabled) {
            def httpBuilder = httpBuilderFactoryService.getInstance()
            httpBuilder.request("$MAILCHIMP_API/lists/batch-subscribe.json", POST, JSON) { HttpRequestBase request ->
                body = [
                    apikey: grailsApplication.config.mailchimp.apiKey,
                    id: grailsApplication.config.mailchimp.listId,
                    batch: [
                            [email: [email: user.email], email_type: 'html']
                    ],
                    double_optin: false,
                    update_existing: true
                ]
                response.success = { res, json ->
                    if (json.error_count > 0) {
                        def error = json.errors[0]?.error ?: 'unknown'
                        log.error("$user.email add to marketing list failed, error: $error")
                    } else {
                        log.info("$user.email added to marking list")
                    }
                }
            }
        }
    }
}

Here’s the Spock specification, which checks for a call to MailChimp. The last parameter to Request calls the builder closure and checks that it has set the expected properties.

@TestFor(MarketingSubscriptionService)
class MarketingSubscriptionServiceSpec extends Specification {

    def 'calls mailchimp'() {
        setup:
        MarketingSubscriptionService service = new MarketingSubscriptionService()
        HTTPBuilder httpBuilder = Mock(HTTPBuilder)
        service.httpBuilderFactoryService = [getInstance: {httpBuilder}]
        service.grailsApplication = [config:[mailchimp:[enabled: 1, apiKey: 'foo', listId: 'bar']]]

        def user = UserFixtures.ernestHemmingway

        when:
        service.subscribe(user)

        then:
        1 * httpBuilder.request(
                'https://us7.api.mailchimp.com/2.0/lists/batch-subscribe.json',
                Method.POST,
                ContentType.JSON,
                {
                    it.delegate = [response: [:]]
                    it.call()
                    it.body.apikey == 'foo' && it.body.id == 'bar' && it.body.batch[0].email.email == user.email
                }) >> null
    }
}

Works great!

Grails MongoDB plugin 1.3.0 rollback

I ran into a nasty little timeout bug with grails/mongo, which I first noticed because my Elastic Beanstalk cluster went unresponsive, but eventually started seeing constantly in local development as well. Not sure what triggers it, but the error is:

Connection wait timeout after 120000 ms. Stacktrace follows:
com.mongodb.DBPortPool$ConnectionWaitTimeOut: Connection wait timeout after 120000 ms

The problem is known and being tracked here and here.

Rolled back to MongoDB plugin 1.2.0, which caused it’s own obscure error due to a change in grails-datastore where domain classes with sequential ID’s are stored differently:

Caused by NumberFormatException: For input string: "facebookUser"

(facebookUser is my domain class collection)

Clearing the facebookUser.next_id collection solved it.

What fun! Hope this gets fixed soon.

Rendering Joda DateTime objects with Grails JSON converter

Hacking on grails, I tried to dump a complex object to JSON as follows:

myComplexObject as JSON

and I got the following error:

Class org.codehaus.groovy.grails.web.converters.marshaller.json.GenericJavaBeanMarshaller can not access a member of class org.joda.time.DateTimeFieldType$StandardDateTimeFieldType with modifiers "public"

Looks like I could use JSON.registerObjectMarshaller(DateTime) in Bootstrap.groovy to fix this, but even easier is to install the jodatime plugin – works like a charm!

Constructor injection in Grails

Had some problems getting constructor injection to work in my grails services.

My service looked like this:

class MyService {
  def grailsApplication
  def thing

  def MyService() {
    thing = new Thing(grailsApplication.config.myConfigVar)
  }

}

but grailsApplication was null in the constructor, resulting in a NullPointerException.

After some digging around I got this to work:

class MyService {
  def grailsApplication
  def thing

  @PostConstruct
  void init() { {
    thing = new Thing(grailsApplication.config.myConfigVar)
  }
}

Learning Grails

I’m digging into Grails, after a couple years using Java and Spring MVC. So far so good!

Some links:

  • Testing Grails – http://fbflex.wordpress.com/2012/12/29/testing-grails/
  • Grails Manual – http://grails.org/doc/latest/