We can unit test constraints in domain objects with the mockForConstraintsTests(domainClass)
method. This will add a validate()
method to our domain class. Normally the validate()
is dynamically added to our domain class when we run our Grails application. But with the mockForConstraintsTests(domainClass)
method we can use it without running a complete Grails application.
Suppose we have the following domain class with a lot of constraints defined for the different properties:
package com.mrhaki.grails.unittest class User { static constraints = { name blank: false, maxSize: 40, notEqual: 'testEquals' nickName blank: false, unique: true, minSize: 4, maxSize: 20, validator: { if (it == 'groovy') { ['invalid.groovyness'] } } avatar nullable: true, validator: {val, obj -> if (val != null) { val != obj.name } } creditCard nullable: true, creditCard: true email nullable: true, email: true, matches: /.*\.nl$/ type nullable: false, inList: ['Admin', 'User'] points nullable: true, max: 1000, min: 100 age nullable: true, range: 12..120 blogURL nullable: true, url: true extra nullable: true, size: 4..25 } String name String nickName String avatar String creditCard String email String type Integer points Integer age String blogURL String extra }
To unit test the constraints we can simply write a unit test class which extends from GrailsUnitTestCase
. Then we must invoke mockForConstraintsTests(User)
so the validate()
method is added to our user
class. Next we can set values for the properties and test if the validation succeeds or fails. We run the tests with the following grails command:
grails test-app -unit
Here is the code for the unit test class:
package com.mrhaki.grails.unittest import grails.test.* class UserTests extends GrailsUnitTestCase { def user protected void setUp() { super.setUp() // Make sure we can invoke validate() on our User domain object. mockForConstraintsTests(User) // Set up default user, so we can easily test single properties. user = new User(name: 'test', nickName: 'tester', type: 'User') } /** * class User{ * ... * nickName unique: true * ... * } */ void testUnique() { // Test user to test uniqueness of nickName property. def test = new User(name: 'tester', nickName: 'tester', type: 'User') mockForConstraintsTests(User, [test]) assertFalse user.validate() assertEquals 'Nickname is not unique.', 'unique', user.errors['nickName'] user = new User(name: 'test', nickName: 'otherTester', type: 'User') assertTrue user.validate() } /** * class User { * ... * name notEqual: 'testEquals' * ... * } */ void testNotEqual() { user.name = 'testEquals' assertFalse user.validate() assertEquals 'Name is equal to testEquals.', 'notEqual', user.errors['name'] user.name = 'test' assertTrue user.validate() } /** * class User { * ... * name blank: false * nickName blank: false * ... * } */ void testBlank() { user = new User(name: '', nickName: '', type: 'Admin') assertFalse user.validate() assertEquals 'Name is blank.', 'blank', user.errors['name'] assertEquals 'NickName is blank.', 'blank', user.errors['nickName'] user = new User(name: 'test', nickName: 'tester', type: 'User') assertTrue user.validate() } /** * class User { * ... * avatar nullable: true * creditCard nullable: true * email nullable: true * type nullable: false * points nullable: true * age nullable: true * blogURL nullable: true * extra nullable: true * ... * } */ void testNullable() { user = new User() assertFalse user.validate() assertEquals 'Name is null.', 'nullable', user.errors['name'] assertEquals 'NickName is null', 'nullable', user.errors['nickName'] assertNull user.errors['avatar'] assertNull user.errors['creditcard'] assertNull user.errors['email'] assertEquals 'Type is null.', 'nullable', user.errors['type'] assertNull user.errors['points'] assertNull user.errors['age'] assertNull user.errors['blogURL'] assertNull user.errors['extra'] } /** * class User { * ... * nickName minSize: 4 * ... * } */ void testMinSize() { user = new User(name: 'test', nickName: 'tst', type: 'User') assertFalse user.validate(); assertEquals 'NickName is not minSize 4.', 'minSize', user.errors['nickName'] user.nickName = 'tester' assertTrue user.validate() } /** * class User { * ... * name maxSize: 40 * nickName maxSize: 20 * ... * } */ void testMaxSize() { user.name = 'This name is just way longer than the maxSize argument defines.' user.nickName = 'The nickName is just too long, people will not remember this.' assertFalse user.validate() assertEquals 'Name is too long.', 'maxSize', user.errors['name'] assertEquals 'NickName is too long.', 'maxSize', user.errors['nickName'] user.name = 'Hubert Klein Ikkink' user.nickName = 'mrhaki' assertTrue user.validate() } /** * class User { * ... * email email: true * ... * } */ void testEmail() { user.email = 'test' assertFalse user.validate() assertEquals 'Not a valid email.', 'email', user.errors['email'] user.email = 'valid@country.nl' assertTrue user.validate() } /** * class User { * ... * email matches: /.*\.nl$/ * ... * } */ void testMatches() { user.email = 'test@country.com' assertFalse user.validate() assertEquals "Doesn't end with .nl.", 'matches', user.errors['email'] user.email = 'test@country.nl' assertTrue user.validate() } /** * class User { * ... * creditCard creditCard: true * ... * } */ void testCreditCard() { user.creditCard = '1234512' assertFalse user.validate() assertEquals 'Not a valid creditcard#.', 'creditCard', user.errors['creditCard'] user.creditCard = '4417123456789113' assertTrue user.validate() } /** * class User { * ... * type inList: ['Admin', 'User'] * ... * } */ void testInList() { user.type = 'NotValid' assertFalse user.validate() assertEquals 'NotValid not in list for type.', 'inList', user.errors['type'] user.type = 'user' assertFalse user.validate() assertEquals 'inType validator is case sensitive.', 'inList', user.errors['type'] user.type = 'User' assertTrue user.validate() } /** * class User { * ... * points max: 1000 * ... * } */ void testMax() { user.points = 20000 assertFalse user.validate() assertEquals '20000 is above max for points.', 'max', user.errors['points'] user.points = 900 assertTrue user.validate() } /** * class User { * ... * points min: 100 * ... * } */ void testMin() { user.points = 10 assertFalse user.validate() assertEquals '10 is below min for points.', 'min', user.errors['points'] user.points = 210 assertTrue user.validate() } /** * class User { * ... * extra size: 4..25 * ... * } */ void testSize() { user.extra = 'ab' assertFalse user.validate() assertEquals "'ab' size is below 4 for extra.", 'size', user.errors['extra'] user.extra = 'This text is too long to be of the correct size.' assertFalse user.validate() assertEquals 'Text is longer than 25 for extra.', 'size', user.errors['extra'] user.extra = 'This is the right length.' assertTrue user.validate() } /** * class User { * ... * age range: 12..120 * ... * } */ void testRange() { user.age = 0 assertFalse user.validate() assertEquals '0 is below range for age.', 'range', user.errors['age'] user.age = 200 assertFalse user.validate() assertEquals '200 is above range for age.', 'range', user.errors['age'] user.age = 40 assertTrue user.validate() } /** * class User { * ... * blogURL url: true * ... * } */ void testURL() { user.blogURL = 'invalid-url' assertFalse user.validate() assertEquals "'invalid-url' is not valid.", 'url', user.errors['blogURL'] user.blogURL = 'http://www.mrhaki.com' assertTrue user.validate() } /** * class User { * ... * nickName validator: { * if (it == 'groovy') { * ['invalid.groovyness'] * } * } * avatar validator: {val, obj -> * if (val != null) { * val != obj.name * } * } * ... * } */ void testCustomValidator() { user.avatar = 'test' assertFalse user.validate() assertEquals "'test' is the same as name.", 'validator', user.errors['avatar'] user.avatar = 'avatar-test' assertTrue user.validate() // Test custom error key. user.nickName = 'groovy' assertFalse user.validate() assertEquals "'groovy' not allowed as nickName.", 'invalid.groovyness', user.errors['nickName'] user.nickName = 'tester' assertTrue user.validate() } }