Archive

Posts Tagged ‘unit’

Unit Testing Delete Actions in Grails

September 4th, 2009 Flo 6 comments

I’m quite new in Groovy and Grails and wanted to share some of my lessons learned while
writing my first unit tests in Grails.

Fortunately at the time I started unit testing in Grails the Testing Plugin had already moved into Grails core.
The documentation is quite good so it’s a good idea to start there.

Today I had a little problem testing the delete method on a Controller. Let’s say I have a Domain class Song which looks like

//  Song.groovy
class Song {
    String name =""
}

I have a method in my Controller that deletes a Song like the delete action from static scaffolding. After calling the action I want to make sure that my count of Songs has decreased.
My first attempt looked like:

// SongTests.groovy
class SongTests extends GrailsUnitTestCase {

    def testSongs
    Song s1, s2

    protected void setUp() {
        super.setUp()
        s1 = new Song(name: "Song 1")
        s2 = new Song(name: "Song 2")
        testSongs = [s1, s2]
        mockDomain(Song, testSongs)
    }

    void testDelete() {
        int oldSize = testSongs.size()
        s1.delete() // Note: Normally this would happen somewhere in the controller action.
        assertEquals oldSize - 1, testSongs.size()
    }
}

But this test struggles to pass. I did some println’s and looked at the testSongs List and noticed that it did not change. After doing some research I found the cause of my problem in MockUtils.groovy

//  MockUtils.groovy
....
    static GrailsDomainClass mockDomain(Class clazz, Map errorsMap, List testInstances = []) {

        ....
        def rootInstances = testInstances.findAll { clazz.isInstance(it) } // findAll creates a new List!!!
        def childInstances = testInstances.findAll { clazz.isInstance(it) && it.class != clazz }.groupBy { it.class }

        TEST_INSTANCES[clazz] = rootInstances
        addDynamicFinders(clazz, rootInstances)
        addGetMethods(clazz, dc, rootInstances)
        addCountMethods(clazz, dc, rootInstances)
        addListMethod(clazz, rootInstances)
        addValidateMethod(clazz, dc, errorsMap, rootInstances)
        addDynamicInstanceMethods(clazz, rootInstances)
        addOtherStaticMethods(clazz, rootInstances)
...
}

private static void addDynamicInstanceMethods(Class clazz, List testInstances) {
...
        // Add delete() method.
        clazz.metaClass.delete = { Map args = [:] ->
            for (int i in 0..<testInstances.size()) {
                if (testInstances[i] == delegate) {
                    testInstances.remove(i)
                    break;
                }
            }
        }
...
}

The problem was, that findAll creates a new list. Since delete just removes the element from the internal list, the original list keeps passed in as second parameter to the mockFor method stays
untouched.

With this in mind the updated test looked like:

    void testDelete() {
        int oldSize = Song.count()
        s1.delete()
        assertEquals oldSize - 1, Song.count()
    }

Which works perfectly.
If you are just getting started too I would recommend you the following sites which give a good introduction to this topic:

The Definitive Guide to Grails Book from Graeme Rocher and Jeff Brown:
http://www.amazon.de/Definitive-Guide-Grails-Experts-Voice/dp/1590597583

Glen Smith’s Blog – MockFor(March) Series:
http://blogs.bytecode.com.au/glen/2008/03/04/mockfor-march—overcoming-grails-testing-inertia.html
http://blogs.bytecode.com.au/glen/2008/03/07/mockfor-march—unit-testing-grails-taglibs.html
http://blogs.bytecode.com.au/glen/2008/03/12/mockfor-march—unit-testing-grails-controllers.html
http://blogs.bytecode.com.au/glen/2008/03/27/mockfor-march—unit-testing-grails-services.html

Groovy Testing guide:
http://docs.codehaus.org/display/GROOVY/Testing+Guide

Make.Go.Now – Ode to MockFor(March)
http://www.make-go-now.com/2009/03/05/ode-to-mockformarch-part-1-testing-constraints/
http://www.make-go-now.com/2009/04/17/ode-to-mockformarch-part-2-mockdomain/

IBM developerWorks:
Mastering Grails: Testing your Grails application
Mastering Grails Series

Delicous in general and my stream
http://delicious.com/
http://delicious.com/fsalbrechter

If you have any suggestions or found an error I’d be happy if you drop me a line.

Categories: grails, groovy, howto Tags: , , ,