Fork me on GitHub

LD.

Music, software, life… and stuff.

[ Twitter ] [ GitHub ] [ LinkedIn ]

Custom Grails Test Types/Phases

Recently there was a query on the grails-user mailing list asking how to create a separate test suite (in the poster’s terminology). In Grails’ terminology, what the poster wanted was a separate test type that could be run separately.

The following is an expansion on the solution offered in the thread.

There are two core concepts in the test system; types and phases.

Test Phases

A test phase refers to the state of the application at test time. Grails comes with 4…

  1. Unit - no application state, only the classpath is configured
  2. Integration - application running (e.g. database, beans), but not listening for http requests
  3. Functional - application is fully running
  4. Other - same as Unit

Here’s how to create your own test phase.

In your scripts/_Events.groovy file…

// 1. add the name of your phase to this variable with this event handler
eventAllTestsStart = {
    phasesToRun << "custom"
}

// 2. Create a «phase name»Tests variable containing a list of test types (more on this later)
customTests = ["custom"]

// 3. Create pre and post closures
customTestPhasePreparation = {
    // called at the start of the phase
}
customTestPhaseCleanUp = {
    // called at the end of the phase
}

Even if you don’t have anything to do in your Preparation and CleanUp closures, you still need to create empty ones.

Note that the name of a test phase has no real correlation to it’s type’s names. In this example we just happen to have a type with name custom in a phase named custom.

Mimicking standard phases

If you need your phase to be like one of the standard phases, just call that phase’s handlers…

customTestPhasePreparation = {
    integrationTestPhasePreparation()
}
customTestPhaseCleanUp = {
    integrationTestPhaseCleanUp()
}

That’s all there is to it.

Test Types

A test type is really two things; a grouping of tests and a test running mechanism.

Above we had…

…
// 2. Create a «phase name»Tests variable containing a list of test types (more on this later)
customTests = ["custom"]
…

The «phaseName»Tests lists contains a phase’s test types. These must either be String's or instance of GrailsTestType. Writing a GrailsTestType implementation will be the subject of a future article (if you are interested, start with the JavaDoc on GrailsTestType and then look at implementations in the wild like the one from the Spock plugin).

Grails ships with the JUnit4GrailsTestType which is what Strings get implicitly converted to (this happens here) for both convenience and legacy reasons.

Test types are associated to a certain directory (under test) and one or more class name suffixes. The JUnit test type uses the suffix Tests pre Grails 1.3 and Tests and Test in Grails 1.3. The directory for String test types is the value (i.e. test/custom for our above example).

Pre and post test type handlers

You don’t need to write pre/post handlers for types like you do with phases. But should you need to, here is how you do it…

eventTestSuiteStart = { typeName ->
    if (typeName == 'custom') {
        // do stuff
    }
}

eventTestSuiteEnd = { typeName ->
    if (typeName == 'custom') {
        // do stuff
    }
}

The event has that name due to legacy reasons.

Types in integration or functional phases

Above we created an integration-like phase called custom. Without a bit of extra work, our tests won’t be completely like standard integration tests in that they won’t have transaction rollback and other bits and pieces. To do this you need to register a JUnit4GrailsTestType so it can be configured. Here is a complete example on how you would do this…

import org.codehaus.groovy.grails.test.junit4.JUnit4GrailsTestType

// 1. add the name of your phase to this variable with this event handler
eventAllTestsStart = {
    phasesToRun << "custom"
}

// 2. Create a custom test type
def testTypeName = "custom"
def testDirectory = "custom"
def testMode = new GrailsTestMode(autowire: true, wrapInTransaction: true, wrapInRequestEnvironment: true)
def customTestType = new JUnit4GrailsTestType(testTypeName, testDirectory, testMode)

// 3. Create a «phase name»Tests variable containing the test type(s)
customTests = [customTestType]

// 4. Create pre and post closures
customTestPhasePreparation = {
    integrationTestPhasePreparation()
}
customTestPhaseCleanUp = {
    integrationTestPhaseCleanUp()
}

For a functional-like phase you would do…

import org.codehaus.groovy.grails.test.junit4.JUnit4GrailsTestType

// 1. add the name of your phase to this variable with this event handler
eventAllTestsStart = {
    phasesToRun << "custom"
}

// 2. Create a custom test type
def testTypeName = "custom"
def testDirectory = "custom"
def testMode = new GrailsTestMode(autowire: true)
def customTestType = new JUnit4GrailsTestType(testTypeName, testDirectory, testMode)

// 3. Create a «phase name»Tests variable containing the test type(s)
customTests = [customTestType]

// 4. Create pre and post closures
customTestPhasePreparation = {
    functionalTestPhasePreparation()
}
customTestPhaseCleanUp = {
    functionalTestPhaseCleanUp()
}

(functional tests don’t need rollback transactions or fake request environments)

For completeness, here’s a unit-like phase (though you could just use a String)…

import org.codehaus.groovy.grails.test.junit4.JUnit4GrailsTestType

// 1. add the name of your phase to this variable with this event handler
eventAllTestsStart = {
    phasesToRun << "custom"
}

// 2. Create a custom test type
def testTypeName = "custom"
def testDirectory = "custom"
def customTestType = new JUnit4GrailsTestType(testTypeName, testDirectory)

// 3. Create a «phase name»Tests variable containing the test type(s)
customTests = [customTestType]

// 4. Create pre and post closures
customTestPhasePreparation = {
    unitTestPhasePreparation()
}
customTestPhaseCleanUp = {
    unitTestPhaseCleanUp()
}

Running your phase/type

Once registered and configured, your custom tests can be run just like any other tests using grails test-app. You can even target them like normal…

grails test-app custom: // run all types in the custom phase
grails test-app :custom // run the custom type in all phases
grails test-app custom:custom // run the custom type in the custom phase

You can even target tests and test methods

grails test-app custom:custom Person* Product.testSave

The above example would run all test classes starting with Person (and ending in Test or Tests) and the testSave test in ProductTest or ProductTests.

The Grails manual has a section on running tests.

Not having your type run by default

The original email query was asking for a solution for having a test type that didn’t run with grails test-app, but ran when explicitly requested. Given the above, the following should make sense…

eventAllTestsStart = { 
    if (testOptions.customOnly) { 
        phasesToRun = ["integration"] // remove the other phases 
        integrationTests = [] // remove the normal integration  tests 
        addCustomTestType() 
    } else if (testOptions.withCustom) { 
        addCustomTestType() 
    } 
} 

addCustomTestType = { 
        integrationTests << "ipbx"
}

(The testOptions map contains all switch or parameter style arguments passed to grails test-app)

So this gives us…

grails test-app // don't run the custom type
grails test-app -only-custom // run ONLY the custom type
grails test-app -with-custom // run ALL types including our custom type

Why isn’t this in the manual?

Laziness on my part. This article will likely be the basis of what will end up in the manual (GRAILS-6301).

Posted: May 20th, 2010 @ 9:21 pm

Tags: #software  #grails  #testing  

Comments

Archive · RSS · Theme by Autumn