Skip to main content

Testing Groovy domain classes

If you are trying to test Grails domain class constraints by putting your unit test cases in the 'test/unit' directory, then your tests will fail because the domain objects will not have the 'valdate' method. This can be resolved in two ways:
  1. Place the test cases inside test/integration (which will slow things down)
  2. Use the method 'mockForConstraintsTests(Trail)' to create mock method in your domain class and continue writing your test cases in 'test/unit'


What follows is some example code around this finding.

I am working on a Groovy on Grails project for a website to help programmers keep up and refresh their skills.

I started with some domain classes and then moved on to write some unit tests. When we create a Grails project using
grails create-app
,
it creates several directories, one of which is a directory called 'test' for holding unit tests. This directory contains two directories, 'unit', and 'integration' for unit and integration tests respectively.

Now begins an interesting journey with writing unit tests in Groovy. I wanted to write tests for my domain classes. A simple domain class like the one I have below can contain only properties and constraints for those properties. This is what Trail.groovy looks like.


class Trail {
String shortName
String name
String description

def hasMany = [learningObjects:LearningObject]

static constraints = {
shortName(maxSize:6, blank:false)
name(maxSize:75, blank:false)
description(maxSize:2048, blank:false)
}
}


I wanted to start writing unit tests to test the constraints. When Grails creates a domain class, it injects several methods in it at runtime , one of which is a method called 'validate'. This method is called before the domain object is saved and it will return a false if the domain object has violated any constraints. So, I created a simple unit test to test the constraints of Trail.groovy

This is what my initial test case looked like.


import grails.test.*

class TrailTests extends GrailsUnitTestCase {

def trail

protected void setUp() {
super.setUp()
trail = new Trail(shortName:'sname',
name:'Java 101',
description:'This is a basic Java course')
}

protected void tearDown() {
super.tearDown()
}

void testShortNameConstraints() {
assertTrue(trail.validate())
trail.shortName = 'thisisaverylongname'
assertFalse(trail.validate())
trail.shortName = ''
assertFalse(trail.validate())
}

//... further methods not shown
}


When I ran my tests using
grails test-app

(See how well Grails integrated testing), I got a bunch of errors that told me that the 'validate' method was not found. If you enjoy looking at stack traces... feast your eyes:

Testcase: testShortNameConstraints took 0.159 sec
Caused an ERROR
No signature of method: Trail.validate() is applicable for argument types: () values: []
groovy.lang.MissingMethodException: No signature of method: Trail.validate() is applicable for argument types: () values: []
at TrailTests.testShortNameConstraints(TrailTests.groovy:20)
at _GrailsTest_groovy$_run_closure4.doCall(_GrailsTest_groovy:203)
at _GrailsTest_groovy$_run_closure4.call(_GrailsTest_groovy)
at _GrailsTest_groovy$_run_closure2.doCall(_GrailsTest_groovy:147)
at _GrailsTest_groovy$_run_closure1_closure19.doCall(_GrailsTest_groovy:113)
at _GrailsTest_groovy$_run_closure1.doCall(_GrailsTest_groovy:96)
at TestApp$_run_closure1.doCall(TestApp.groovy:66)
at gant.Gant$_dispatch_closure4.doCall(Gant.groovy:324)
at gant.Gant$_dispatch_closure6.doCall(Gant.groovy:334)
at gant.Gant$_dispatch_closure6.doCall(Gant.groovy)
at gant.Gant.withBuildListeners(Gant.groovy:344)
at gant.Gant.this$2$withBuildListeners(Gant.groovy)
at gant.Gant$this$2$withBuildListeners.callCurrent(Unknown Source)
at gant.Gant.dispatch(Gant.groovy:334)
at gant.Gant.this$2$dispatch(Gant.groovy)
at gant.Gant.invokeMethod(Gant.groovy)
at gant.Gant.processTargets(Gant.groovy:495)
at gant.Gant.processTargets(Gant.groovy:480)


After some Googling I realized that the 'validate' method is injected into domain objects by the Grails framework, and therefore it does not exist when we instantiate the domain object from test code. I later found that if a test is placed inside 'test/integration' then the test cases are created as the objects would have been created by the Grails framework. So the domain objects would have the 'validate' method. But I also read in another place (I cannot remember where), that if possible it is k better to mock these special methods instead of putting tests in the integration directory, since these tests take longer to instantiate and can slow down the overral running time. And I found in yet another place that there exists a method called 'mockForConstraintsTests(Trail)' which will inject the 'validate' method along with some other methods. So, I added this mock method to my setup after which I was able to run my tests successfully.


protected void setUp() {
super.setUp()
mockForConstraintsTests(Trail)
trail = new Trail(shortName:'sname',
name:'Java 101',
description:'This is a basic Java course')
}


The rest of the class is the same.

So to summarize, if you are trying to test constraints in Grails domain classes, you have two options:
  1. Place the test cases inside test/integration (which will slow things down)
  2. Use the method 'mockForConstraintsTests(Trail)' to create mock method in your domain class and continue writing your test cases in 'test/unit'
I will post more such episodes of how Google helped me resolve issues I faced while developing :-)

Comments

Anonymous said…
Hi,
i have the same problem when testing domain classes.
I tried your solution with mock object but i receive the same error.
The only way i was able to run the test was to move all the files into the integration folder.
have you find any other ways to solve this?
Parag said…
Hi, I have not found any other way. However, I am surprised that mocking did not insert that method.

Do you get the exact same error?

--
Parag
http://www.adaptivelearningonline.net
Anonymous said…
Yes,

i got the same error

No signature of method: Person.save() is applicable for argument types: () values: []

groovy.lang.MissingMethodException: No signature of method: Person.save() is applicable for argument types: () values: []
at PersonTests.testSomething(PersonTests.groovy:14)
at _GrailsTest_groovy$_run_closure4.doCall(_GrailsTest_groovy:203)
at _GrailsTest_groovy$_run_closure4.call(_GrailsTest_groovy)
at _GrailsTest_groovy$_run_closure2.doCall(_GrailsTest_groovy:147)
at _GrailsTest_groovy$_run_closure1_closure19.doCall(_GrailsTest_groovy:113)
at _GrailsTest_groovy$_run_closure1.doCall(_GrailsTest_groovy:96)
at TestApp$_run_closure1.doCall(TestApp.groovy:66)
at gant.Gant$_dispatch_closure4.doCall(Gant.groovy:324)
at gant.Gant$_dispatch_closure6.doCall(Gant.groovy:334)
at gant.Gant$_dispatch_closure6.doCall(Gant.groovy)
at gant.Gant.withBuildListeners(Gant.groovy:344)
at gant.Gant.this$2$withBuildListeners(Gant.groovy)
at gant.Gant$this$2$withBuildListeners.callCurrent(Unknown Source)
at gant.Gant.dispatch(Gant.groovy:334)
at gant.Gant.this$2$dispatch(Gant.groovy)
at gant.Gant.invokeMethod(Gant.groovy)
at gant.Gant.processTargets(Gant.groovy:495)
at gant.Gant.processTargets(Gant.groovy:480)

I use grails 1.1.2 and java6

It's strange all the grails tutorials i found don't mention this problem.
Parag said…
I wish I could you, but I do not know what could be causing this problem.

--
Regards
Parag
Burak Dede said…
Try using "grails clean" and do saving again.
Nirav Assar said…
Parag, thanks for the post. It worked for me. I used mockForConstraintsTests(Race) for my Race object that I was testing for constraints. This worked in the unit test folder.
Parag said…
Hey Nirav, That's great. I am glad it helped.
Unknown said…
For those who find this, make sure you are inheriting GrailsUnitTestCase and not the default GroovyTestCase to make mockForConstraintsTests work!
Parag said…
Thanks Jason.

Earlier someone posted a comment about the 'mockForConstraintsTests' method not working for them.

Perhaps they were using the GroovyTestCase instead of GrailsUnitTestCase.
mani said…
hey thnx dude....it helped a lot....and could you please give me some links where i can find useful grails tutorials or some books (not paid ofcourse :P) looking forward
thnx
Parag said…
Hi Mani, Thanks, I am glad the tutorial helped you.

It has been a while since I have worked on Grails, so I am not aware of the recent set of good tutorials or books. Their website has some links which might be useful to get started

http://grails.org/Tutorials

Popular posts from this blog

Commenting your code

Comments are an integral part of any program, even though they do not contribute to the logic. Appropriate comments add to the maintainability of a software. I have heard developers complain about not remembering the logic of some code they wrote a few months back. Can you imagine how difficult it can be to understand programs written by others, when we sometimes find it hard to understand our own code. It is a nightmare to maintain programs that are not appropriately commented. Java classes should contain comments at various levels. There are two types of comments; implementation comments and documentation comments. Implementation comments usually explain design desicisions, or a particularly intricate peice of code. If you find the need to make a lot of implementation comments, then it may signal overly complex code. Documentation comments usually describe the API of a program, they are meant for developers who are going to use your classes. All classes, methods and variables ...

Inheritance vs. composition depending on how much is same and how much differs

I am reading the excellent Django book right now. In the 4th chapter on Django templates , there is an example of includes and inheritance in Django templates. Without going into details about Django templates, the include is very similar to composition where we can include the text of another template for evaluation. Inheritance in Django templates works in a way similar to object inheritance. Django templates can specify certain blocks which can be redefined in subtemplates. The subtemplates use the rest of the parent template as is. Now we have all learned that inheritance is used when we have a is-a relationship between classes, and composition is used when we have a contains-a relationship. This is absolutely right, but while reading about Django templates, I just realized another pattern in these relationships. This is really simple and perhaps many of you may have already have had this insight... We use inheritance when we want to allow reuse of the bulk of one object in other ...

Planning a User Guide - Part 3/5 - Co-ordinate the Team

Photo by  Helloquence  on  Unsplash This is the third post in a series of five posts on how to plan a user guide. In the first post , I wrote about how to conduct an audience analysis and the second post discussed how to define the overall scope of the manual. Once the overall scope of the user guide is defined, the next step is to coordinate the team that will work on creating the manual. A typical team will consist of the following roles. Many of these roles will be fulfilled by freelancers since they are one-off or intermittent work engagements. At the end of the article, I have provided a list of websites where you can find good freelancers. Creative Artist You'll need to work with a creative artist to design the cover page and any other images for the user guide. Most small to mid-sized companies don't have a dedicated creative artist on their rolls. But that's not a problem. There are several freelancing websites where you can work with great creative ...