views:

455

answers:

2

Unit testing Abstract classes in Groovy

I asked a question previous about unit testing and mocking a domain class, but I do not think I was specific enough. I have a domain class:

package toplevel.domain

abstract class Party {
    static hasMany = [roles:PartyRole]
    static constraints = {
        roles(nullable:true)
        dateCreated(display:false)
        lastUpdated(display:false)
    }
    List roles
    Date dateCreated
    Date lastUpdated
}

Here is my unit test:

import grails.test.*
import toplevel.domain.*

class PartyTests extends GrailsUnitTestCase {
    Party party
    protected void setUp() {
        super.setUp()
        party = [:] as Party
        mockForConstraintsTests(Party, [party])
    }

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

    void testNullRolesIsValid() {
        party.roles = null
        assertTrue "The roles should be nullable", party.validate()
    }
}

Here are the test results: Could not create a new instance of class [toplevel.domain.Party]!

org.codehaus.groovy.grails.exceptions.NewInstanceCreationException: Could not create a new instance of class [toplevel.domain.Party]! at grails.test.MockUtils.prepareForConstraintsTests(MockUtils.groovy:540) at grails.test.MockUtils$prepareForConstraintsTests.call(Unknown Source) at grails.test.GrailsUnitTestCase.mockForConstraintsTests(GrailsUnitTestCase.groovy:111) at PartyTests.setUp(PartyTests.groovy:9) 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) Caused by: java.lang.InstantiationException

I do not understand. I created an instance of the class and gave it to the mockForConstraintsTests method. What am I doing wrong?

+1  A: 

You need to provide a concrete Party class, the test is trying to create an instance of the Party class and can not since it's abstract. I've re-worked your test below and commented the places where I made changes.

package toplevel.domain

import grails.test.*
import toplevel.domain.*

// Create a stub implementation class
class PartyImpl extends Party { }

class PartyTests extends GrailsUnitTestCase {
    Party party
    protected void setUp() {
        super.setUp()
        //party = [:] as Party
        // Create an instance of the stub'd class
     party = new PartyImpl()
        //mockForConstraintsTests(Party, [party])
     // Need to pass in the concrete class as first arg
     mockForConstraintsTests(PartyImpl, [party])
    }

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

    void testNullRolesIsValid() {
        party.roles = null
        assertTrue "The roles should be nullable", party.validate()
    }
}
John Wagenleitner
+1  A: 

This is actually an issue with the way that the mockForConstraintsTests stuff works in grails rather than an issue with using that type of mock generally in groovy.

This type of mock just isn't compatible with the mocks that are being created by the mockForConstraintsTests. If you want to use this library, John is correct about just creating and passing in a simple concrete impl of the class.

I'm actually not a big fan of the constraint mocking stuff that's in recent versions of grails as it isn't "real" and so much of the mocked out stuff is different compared to the actual code that gets run when connecting to a real database. I prefer to use integration tests to test out this kind of constraint.

If you put your same test class in the integration tests and remove the mockForConstraintsTests call, your code will work:

package toplevel.domain

import grails.test.*

class PartyTests extends GrailsUnitTestCase {
    Party party
    protected void setUp() {
        super.setUp()
        party = [:] as Party
    }

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

    void testNullRolesIsValid() {
        party.roles = null
        assertTrue "The roles should be nullable", party.validate()
    }
}

results:

Running 1 integration test...
Running test PartyTests...PASSED
Tests Completed in 226ms ...
-------------------------------------------------------
Tests passed: 1
Tests failed: 0
-------------------------------------------------------
Ted Naleid