Ode to MockFor(March): Part 1 - Testing Constraints


Last March, Glen Smith did a wonderfull series on how to write unit tests for your Grails application. This series addressed a major pain point in Grails development at the time: Testing.

A year later and testing your Grails application is much less painfull thanks to the Testing Plugin and its author Peter Ledbrook which has been now been integrated into Grails 1.1

This series is ment to give new users of Grails an overview of how to do Unit Testing using the Testing Plugin (pre Grails 1.1) or just Grails 1.1, and to highlight what the plugin gives you. This will not be an account of what you had to do prior to the Testing Plugin. Just be thankfull you don’t have to travel that road, I know I’m happy to be off it.

First off: Testing Constraints in your Domain Class

Initally I thought of constraint testing as “too simple to fail”, like testing getters or setters in Java. Plus back in the day (8 months ago) this was a gigantic pain to even think about doing it properly. Now it’s so easy that you might as well do it.

Setting constraints in your domain class do 3 things for you.

  1. Some influence how your database is constructed:

    • inList
    • max
    • min
    • maxSize
    • minSize
    • nullable
    • range
    • scale
    • size
    • unique
  2. Some provide a “test before save” check:

    • blank
    • creditCard
    • email
    • matches
    • url
    • validator
  3. All give you an way to order your fields when used with scaffolding.

So lets say we have this Person Domain Object:

class Person{
    String firstName 
    String lastName
    String userName
    String password
    Address address

    static constraints = {
        firstName(nullable:false, blank:false, max:50, min:2)
        lastName(nullable:false, blank:false)
        userName(nullable:false, blank:false, unique:true)
        password(nullable:false, blank:false, min:8, max:20, matchs:/[a_zA-Z1-0]+/)
    }
}

Here is how we would test the constraints:

class PersonConstraintsTest extends GrailsUnitTestCase{ //1

    def person

    void setUp(){
        super.setup() //2
        mockForConstraintsTests(Person) //3
        person = new Person(firstName:"Jon", 
                lastName:"Smith", 
                userName:"jonsmith", 
                password:"weakpassword")  //4
    }

    void testFirstNameNullable_Pass(){
        assertTrue 'validation shoud have passed ' , person.validate() //5
    }

    void testFirstNameNullable_Fail(){
        person.firstName = null
        assertFalse 'validation shoud have faild' , person.validate() //5
        assertEqual 'should have a nullable error', 'nullable', person.errors["firstName"] //6
    }

    //...

    void tearDown(){
        super.tearDown() //7
    }
}
  1. Extend GrailsUnitTestCase: GrailsUnitTestCase extends GroovyTestCase
  2. Call the parent classes setUp() method. IMPORTANT: You must do this to get all the testing goodness to work.
  3. Call the mockForConstraintsTests method passing in the our domain class. NOTE: This is the class not an instanciatied object.
  4. Set the happy path for the domain object. This instance will pass all constraints. (For domain classes that have a managable set of properties I like taking a “create Golden Master an”
  5. Call the ‘validate’ method on the domain and get a Boolean result
  6. Here we look at the Domain objects error map and see if it has a ‘nullable’ error on the ‘firstName’ field value.
  7. Call super.tearDown(): this will roll back the metaClass of the domain object to it’s unmodified state. Probably a good thing to do.

You would then follow this pattern for the rest of the constraints.

So that’s how you test your constraints. Super easy.

Now here is what the plug in gives you:

By extending the GrailsUnitTestCase and calling the super.setUp() method we get a map to hold our errors.

Here is what Grails does when we call the mockForConstraintsTests method and pass it our Domain class:

  1. check the class to make sure it is a Domain Class
  2. instanciates a new DefaultGrailsDomainClass
  3. adds the “validate” method to the domain object.
  4. builds up the constraint list from all constraints in the domain classes hierarchy.
  5. builds up the constraint list for all the properties constraints in the domain class hierarchy. This gives up us cascading validation in our tests. Very cool.
  6. adds data binding to the domain class constructor
  7. finally adds all of the error related methods (ie. getErrors, hasErrors, setErrors) on the domain.

All the metaClass mojo is taken care of for you and all you have to write is 3 little lines:

  1. super.setUp()
  2. mockForConstraintsTests([Domain])
  3. super.tearDown()

P.S. All of this works just the same for testing the constraints on your Command Objects.

If you want to further dig into all of this testing goodness; download the Grails 1.1 souces and look at these classes: - GrailsUnitTestCase - Mockutils

Next up: mockDomain()

,

  1. No comments yet.
(will not be published)
  1. No trackbacks yet.