Code for this blog can be found on github at
https://github.com/galapagosfinch

Saturday, May 28, 2011

Constraint tests in Grails

Constraints are an integral part of using Domain classes in Grails. Setting limits on values makes the app stronger by detecting unexpected inputs. (What's that phrase, "expect the unexpected"?) But another integral part of all Java development is insuring that the app is correct via unit tests. Constraints are easy to test, but often overlooked. Consider this, however: how much of your business logic is depending on those constraints?  How much of the code in those constraints are your business logic?  Seems foolish not to test them, now, doesn't it?

Consider this simple class:

package com.springminutes.example

class Cow {

    Breed breed
    String color
    Long legs

    static constraints = {
        color(blank:false, nullable: false)
        legs(nullable: false, validator: { Long l, Cow c ->
            def maxLegs = (c.breed == Breed.Guernsey ? 4 : 10)
            c.legs < 0 || c.legs > maxLegs ? "invalid.range" : null
        })
    }
}

The breed and color properties aren't that interesting, but we should test them nonetheless.  When building constraint tests, Grails gives us a simple way to get the constraints up and running:

class CowTests extends GrailsUnitTestCase {

void testConstraints() {
mockForConstraintsTests(Cow)

mockForConstraintsTests outfits a domain class with all of the validation plumbing needed to test to see if your expectations are correct. What next? Let's start with a positive test:

// Good cow
def cow1 = new Cow(breed: Breed.TasmanianGrey, color: "mottled", legs: 7)
assertTrue cow1.validate()
assertEquals 0, cow1.errors.allErrors.size()

We set all the properties to valid values, assert that validation succeeds and (just to be a little more thorough) assert that there are no errors attached to the object. What next? Negative tests!

// Cow with unknown legs
def cow2 = new Cow(breed: Breed.TexasLonghorn, color: "blue")
assertFalse cow2.validate()
assertEquals "nullable", cow2.errors['legs']

Here we have a Cow without setting the number of legs. We expect validation to fail, and for the "nullable" error to be attached to the legs.

// Cow with blank color
def cow3 = new Cow(breed: Breed.TexasLonghorn, color: "", legs: 1)
assertFalse cow3.validate()
assertEquals "blank", cow3.errors['color']

Here we've set all the properties, but the color is an empty string. We expect validation to fail, and for the "blank" error to be attached to the color.

// Custom validator
// Negative legs
def cow4 = new Cow(breed: Breed.Guernsey, color: "wonky", legs: -1)
assertFalse cow4.validate()
assertEquals "invalid.range", cow4.errors['legs']
// Too many legs for a Guernsey
cow4.legs = 7
assertFalse cow4.validate()
assertEquals "invalid.range", cow4.errors['legs']
cow4.breed = Breed.Holstein
// same cow, after correcting the breed
assertTrue cow4.validate()
assertNull cow4.errors['legs']

Finally, that custom validator. We set up our Cow to be a Guernsey with -1 legs. (I guess he's already paid an arm on his mortgage and only owes a leg, har har). We expect validation to fail and for an "invalid.range" error code to be attached to the legs. Next, we set the number of legs to 7 (Guernseys would *never* be caught dead with more than four legs). Again, we expect validation to fail with the same error message. Finally, we change the breed (Holsteins are not particular) and rerun the validation. The legs property is fine now, and we expect no error attached to it.

Constraint validation is an important part of your model, and you should definitely not skimp on testing that business logic.

So, how much do you test your constraints?

Friday, May 27, 2011

Adding Services to Grails Controller Templates

One of the first things we learn in grails is that it can auto-generate domain classes, controllers, services, yadda yadda yadda.  What we *don't* usually learn is that the template for controllers is really minimal - in fact, it was even buggy during certain releases.  The Controller code that is generated does not make use of Service classes, so in your hurry to see something happen, you end up with business logic in the controller when it really should have been relegated somewhere else.

I'm assuming you already know how to create domain classes, controllers, and services. So, before you actually do that, first thing to do is to fix that in the template.  Install the templates via

grails install-templates

This will populate the direct src/templates/ with three subfolders.
artifacts
Contains the template files for "create-*" commands; relies on dynamic scaffolding
scaffolding
Contains the template files for "generate-*" commands; contains everything you need
war
Contains the web.xml file which will control the startup/configuration of your war file (we'll come back to this later)
So, to make your controllers start using services, modify the beginning of the scaffolding/Controller.groovy file to have a service injected:

<%=packageName ? "package ${packageName}\n\n" : ''%>class ${className}Controller {

    static allowedMethods = [save: "POST", update: "POST", delete: "POST"]
// Add the service that matches the controller
    ${className}Service ${domainClass.propertyName}Service

    def index = {
        redirect(action: "list", params: params)
    }

and use it in the methods:

def save = {
    def ${propertyName} = new ${className}(params)
    // below, use service instead of direct instance call
    if (${domainClass.propertyName}Service.save(flush: true)) {
        flash.message = "\${message(code: 'default.created.message', args: [message(code: '${domainClass.propertyName}.label', default: '${className}'), ${propertyName}.id])}"
        redirect(action: "show", id: ${propertyName}.id)
    }
    else {
        render(view: "create", model: [${propertyName}: ${propertyName}])
    }
}

So what's all the fuss?  All we did was replace a direct call to "save" with a delegation to a service which will most likely just call "save".  Having a service gives you (a) a transaction around your work, (b) a clear separation of concerns, where Service does the work and Controller manages connecting Model to View, and (c) reusable code in case you need to use it elsewhere, like a web service or Spring remoting.

Remember, this is the template.  I'm not saying that every Controller needs a matching Service, I'm just saying that starting from that point will probably get you better separation of code in the end.  Cleaner code is more maintainable.  speaking as someone who lead a project where all business logic was embedded in the controllers for the first five months, well, let's just say that I wish someone had sent me a link that looked like this when we were just starting...

The Controller methods can get much more complex; we'll talk about REST later and see how to put your data out there in JSON, XML, and "whatnot".

Friday, May 20, 2011

And so it begins...

... or began, that is.

Back in July 2009, I was lucky enough to be hired onto a Groovy/Grails project, despite the fact that the extent of my experience with Grails was an hour on a Saturday night writing an Address Book app. (Oh, and most of the hour went towards integrating the app with the Yahoo! Maps API.) What began then ended up with me leading a team of ten developers at its height, designing and implementing an application that heavily utilized SpringSource's products - Spring Security to integrate with Atlassian Crowd, Spring Web Service to integrate with the external partners, Spring Integration to automate responses in our test app, and of course, Spring Webflow for our most complex screen interactions, and of course, just about all of Core Spring, which is what Grails is founded upon.

It's been a great learning experience, and I hope to use this forum to describe what I've learned about these products, as well as the rest of the Spring offerings.

And I promise to never say I'll "Spring into Action". Except right now.