Fork me on GitHub

LD.

Music, software, life… and stuff.

[ Twitter ] [ GitHub ] [ Linked In ]

Annotation Driven Extensions With Spock

The following is an introduction to writing extensions with everybody’s favourite testing tool Spock. Like all things in Spock, extensions are easy to use and require little ceremony. We are going to build an extension that times various parts of test execution and blindly writes the times to standard out.

Please note that the API we are going to go through should not be considered stable/final.

We are building against Spock 0.5-SNAPSHOT and the following APIs may change on the road to 1.0.

Annotation Driven

Our extension is going to be annotation driven. We are going to provide an annotation that users can place on various parts of their specs. At the core of this is the IAnnotationDrivenExtension contract…

package org.spockframework.runtime.extension;

import java.lang.annotation.Annotation;
import org.spockframework.runtime.model.*;

public interface IAnnotationDrivenExtension<T extends Annotation> {
  void visitSpecAnnotation(T annotation, SpecInfo spec);
  void visitFeatureAnnotation(T annotation, FeatureInfo feature);
  void visitFixtureAnnotation(T annotation, MethodInfo fixtureMethod);
  void visitFieldAnnotation(T annotation, FieldInfo field);
  void visitSpec(SpecInfo spec);
}

You’ll notice that (almost) all of Spock’s internals are implemented in Java. In this exercise we will implement our extension bits in Groovy.

Annotation driven extensions implement the Visitor Pattern, and Spock will invoke your visitation method for each annotation of your type present on the spec (We’ll discuss the visitSpec() method later).

These methods all take the annotation instance and an info object which describes the annotated thing. For more detail on each of these info types above what we will go through in this exercise, check them out in the Spock source. They are pretty straightforward.

The Annotation

We need to wire our annotation up to our extension. We declare our annotation like normal, but annotate it with the IAnnotationDrivenExtension implementation that backs it.

import java.lang.annotation.*
import org.spockframework.runtime.extension.ExtensionAnnotation

@Retention(RetentionPolicy.RUNTIME)
@Target([ElementType.TYPE, ElementType.METHOD])

@ExtensionAnnotation(TimingExtension)

public @interface Time {}

Here we have declared a new annotation called @Time that takes no parameters and can be placed on types (in our context, the spec class) and methods. The @ExtensionAnnotation meta annotation is how tell Spock who deals with the @Time annotation.

The Extension

Now we need to actual implement our extension. As you would expect, Spock provides a AbstractAnnotationDrivenExtension super class that we can use. This allows us to only implement the methods we need to. In our case, we don’t need the visitFieldAnnotation() method.

import org.spockframework.runtime.extension.AbstractAnnotationDrivenExtension
import org.spockframework.runtime.model.SpecInfo
import org.spockframework.runtime.model.FeatureInfo
import org.spockframework.runtime.model.MethodInfo

class TimingExtension extends AbstractAnnotationDrivenExtension<Time> {
  def timeSpec = false
  def timedFeatures = []

  void visitSpecAnnotation(Time annotation, SpecInfo spec) {
    timeSpec = true
  }

  void visitFeatureAnnotation(Time annotation, FeatureInfo feature) {
    timedFeatures << feature.name
  }

  void visitSpec(SpecInfo spec) {
    spec.addListener(new TimingRunListener(timeSpec, timedFeatures)
  }
}

Here we simply collect information on which things we are timing, then add a run listener (explained below) that will do the timing.

The implementation of the visitation methods in AbstractAnnotationDrivenExtension always throw an org.spockframework.runtime.InvalidSpecException. Our annotation can legally be placed on any fixture methods according to it’s definition, but that doesn’t make sense in the context of our extension. Because we have not overridden the visitFixtureAnnotation() method in our extension, if the user annotates a fixture method with @Time, they will get a nice error message from Spock that tells them that that is invalid.

It’s important to consider that there is guaranteed to only ever be one instance of an extension for each spec. Spock will create an instance of your extension the first time it encounters your annotation, and then reuse that instance when it encounters later annotations (in the same spec).

Listening In

Spock has it’s own execution listening API specified by IRunListener.

package org.spockframework.runtime;

import org.spockframework.runtime.extension.builtin.StepwiseExtension;
import org.spockframework.runtime.model.*;

public interface IRunListener {
  void beforeSpec(SpecInfo spec);
  void beforeFeature(FeatureInfo feature);
  void beforeIteration(IterationInfo iteration);
  void afterIteration(IterationInfo iteration);
  void afterFeature(FeatureInfo feature);
  void afterSpec(SpecInfo spec);
  void error(ErrorInfo error);
  void specSkipped(SpecInfo spec);
  void featureSkipped(FeatureInfo feature);
}

This is all pretty straightforward, but you may be wondering about beforeIteration() and afterIteration(). This are for parameterised (or data-driven) feature methods and are called for each set of parameters.

Again, we have convenient super class to work with.

import org.spockframework.runtime.AbstractRunListener;
import org.spockframework.runtime.model.*;

class TimingRunListener extends AbstractRunListener {

  def timeSpec = false
  def specStartTime
  def featureStartTimes = [:]

  TimingRunListener(timeSpec, timedFeatures, timedFixtures) {
    this.timeSpec = timeSpec
    timedFeatures.each { featureTimes[it] = null }
  }

  private now() {
    System.currentTimeMillis()
  }

  private start(name) {
    println "starting $name …"
  }

  private done(name, started, finished = now()) {
    println "$name took ${finished - started} milliseconds"
  }

  void beforeSpec(SpecInfo spec) {
    if (timeSpec) {
      specStartTime = now()
      start("spec '$spec.name'")
    }
  }

  void beforeFeature(FeatureInfo feature) {
    if (featureStartTimes.containsKey(feature.name)) {
      featureStartTimes[feature.name] = now()
      start("feature '$feature.name'")
    }
  }

  void afterFeature(FeatureInfo feature) {
    def startedAt = featureStartTimes[feature.name]
    if (startedAt) {
      echo("feature '$feature.name'", startedAt)
    }
  }

  void afterSpec(SpecInfo spec) {
    if (specStartTime) {
      echo("spec '$spec.name'", specStartTime)
    }
  }
}

Our extension is now complete… let’s try it out.

In Action

Now all we have to do is annotate our spec…

import spock.lang.*

@Time
class DoingStuff extends Specification {

  @Time
  def "a b c"() {
    when:
    Thread.sleep(1000)
    then:
    true
  }

}

If we were to run this spec we would see something like…

starting spec 'DoingStuff' …
starting feature 'a b c' …
feature 'a b c' took 1000 milliseconds
spec 'DoingStuff' took 1500 milliseconds

Obviously the times are made up but you get the point.

The Real World

Spock ships with a bunch of extensions (as of today in 0.5-SNAPSHOT):

For more info on any of these extensions, check out their javadoc comments.

You could very easily make your own extensions specific to your projects to make your life easier. You could load data fixtures, establish connections… all kinds of things.

Summary

So that’s “Annotation Driven Extensions” with the Spock Framework.

Go forth and create some… and may the force be with you (wait… what?)

Posted: Aug 18th, 2010 @ 10:58 pm

Tags: #groovy spock  

Comments

Proposal: Grails Plugin Collective

The following is a proposal for, and a request for comment on, the idea of a semi-structured combination of social and technological systems and norms to initiate, develop and maintain Grails plugins.

The Problem Being Addressed

Typically, a plugin is born through inspiration or necessity. It’s not uncommon that the original author at some point stops maintaining or developing a frequently-used or promising plugin. There are many reasons for this and they are in no way unique to the Grails ecosystem. People contribute and move on as is their want and well intentioned developers can quickly lose interest in developing a plugin due to roadblocks and lack of knowledge, experience and feedback.As a Software Developer using Grails professionally, the availability of a high quality plugin makes a significant product difference; and conversely an unsuccessful attempt to use a plugin due to bugs or lack of documentation costs real time. Often when finding an issue and fixing it, getting that fix back into the community takes considerable effort in coordinating with the plugin developer/maintainer who may or may not be around.

As a plugin developer, during busy times it can be hard to keep up with bug reports, feature requests and Grails upgrades. It’s also concerning that knowledge and understanding of the plugins I have authored rest largely with myself, creating a single point of failure. It would be much better if there were other people who could (and did) maintain and improve things in my (or any other author’s) absence.

The Proposed Solution

To remedy this situation, I propose that a self-organised, distributed, democratic, collective be formed… to foster, develop and maintain Grails plugins. I am calling this idea the “Grails Plugin Collective” for the time being.

The social structure is what would be the most interesting and, ultimately, enabling. It would allow members to work across plugins, scratching their own itches, and getting those fixes/features into release with minimal effort without risking quality. It would also enable steering and guiding the direction of plugins while allowing members to freely propose ideas and alternatives. Due to Grails’ “convention over configuration” nature, this becomes critically important. Hopefully, the collective wisdom of a group of individuals results in a highly consistent, conventional approach to how plugins work.

Goals and Aims

Relationship with VMWare and certification

I propose that the collective should be an entity outside of VMWare. This does not exclude VMWare employees from participating in the collective by any means, or from having some kind of special role. It purely means that the entity is not bound to VMWare in any way other than through the licensing arrangement of any software owned by VMWare.

I also propose that if a plugin certification scheme is ever established, it be done completely separately to the collective. I imagine that many of the collectivised plugins would aim for certification if at all possible, but in this respect they would be no different to other community plugins.

Procedures and Principles

The collective should aim to operate with the least amount of bureaucracy and procedure that is required. The core idea is that as a democratic structure, all proposals are put to a vote. Members will have a relatively short time frame (proposed: 48 hours) to vote. A certain proportion of the votes cast must be affirmative for the proposal to succeed. It’s likely that there would also be some sort of veto mechanism for things that are extremely time critical.

I image the following kinds of things would be voted on:

Initial Membership

Being that what is being proposed is a self-organising, democratic social structure, I am hesitant to be to prescriptive about how such a thing might work in terms of my vision. I am interested in getting other people’s ideas.

An initial membership also needs to be put together. This will be somewhat of a challenge for a democratic outfit. To get the ball rolling, I suggest that the VMWare Grails team propose an initial membership of roughly 10 people (likely including themselves). From there, the collective can self organise and induct new members over time.

So what are your thoughts? Is such an initiative worthwhile? Could it work? Could it succeed in raising and maintaining the quality of selected Grails plugins? Are there other such initiatives that we could learn from? What kind of social practices could be put in place to support rapid development while maintaining consistency and quality?

Feel free to leave your opinion either here or on the grails-user mailing list.

Posted: Jun 7th, 2010 @ 9:29 pm

Tags: #software  #grails  

Comments

Putting the Artefact API to work in your Grails app

I haven’t seen many cases of people putting the Grails’ artefact API to use in their application. It’s likely that most Grails’ users aren’t aware that it exists, which in many ways is a good thing… it is after all an implementation concern.

You can however put it to work in your own application.

What is it?

Perhaps it’s best to discuss what an artefact is in Grails first. An example of an artefact is a service class. Think of what happens when your grails application starts; it finds all the service classes, creates an instance of each (typically) and autowires the dependencies. Grails defines a class whose instances represent a single service class and an associated handler. This is what I am collectively calling the Artefact API.

Why use it?

The biggest reason these days is by far the hot reloading support. That is, if you change an artefact class while the application is running, Grails (with some help from you) will reload the class and you get the changes without a restart. Of course, we are all familiar with this.

There is a more subtle benefit though. You may have some kind of prominent & pervasive concept in your application (like a service is a concept). You might find that creating an artefact for this concept can make your life easier.

An example use case

Consider some kind of eStore, with a wide range of products. Our products have simple persistence requirements but can have wildly varying behaviours in the system, which are likely to get more complex over time. I try to avoid extensive subclassing of domain objects when the classes primarily vary in behaviour and not data. So we need to find another way to implement the varying behaviour.

The ProductHandler

Each product is going to provide a handler (terrible name I know) that is responsible for the majority of the product’s behaviour. Here is our product domain class…

class Product {
    String name
    
    // don't want to try and persist the handler
    static transients = ['handler'] 
    
    ProductHandler getHandler() {
        // somehow get the handler for this product
    }
}

And we have a ProductHandler interface for good measure…

interface ProductHandler {
    void setProduct(Product)
    boolean isCanBeSold()
}}

A non artefact solution

One kind of traditional approach would be to have each product persist a key that refers to a particular product handler, and to have each product handler class register itself with some kind of registry.

class Product {
    String name
    String handlerKey
    
    def productHandlerService // autowired 
    
    // don't want to try and persist the handler
    static transients = ['handler']
    
    protected handler
    ProductHandler getHandler() {
        if (!handler) {
            handler = productHandlerService[this]
        }
        handler
    }
}

// Our registry
import grails.spring.BeanBuilder
class ProductHandlerService {
    def grailsApplication // autowired
    static private registry = [:].asSynchronized()
    
    static register(String key, Class handlerClass) {
        registry[key] = handlerClass // should check that handlerClass implements ProductHandler
    }
    
    protected createBeanBuilder() {
        new BeanBuilder(grailsApplication.mainContext, grailsApplication.classLoader)
    }
    
    protected instantiateHandler(handlerClass, Product product) {
        createBeanBuilder().with {
            beans {
                handler(handlerClass, product: product) {
                    // autowire the handler so it can use services etc.
                    it.autowire = true
                }
            }
            createApplicationContext().getBean('handler')
        }
    }
    
    def getAt(Product product) {
        def handlerClass = registry[product.handlerKey] // assume there is always a handler
        instantiateHandler(handlerClass, product)
    }
}

// Example Handler (in src/groovy dir)
class OnlyOnTuesdayProductHandler implements ProductHandler {
    static {
        ProductHandlerService.register('onlyOnTuesday', OnlyOnTuesdayProductHandler)
    }
    Product product
    boolean isCanBeSold() {
        new GregorianCalendar().get(Calendar.DAY_OF_WEEK) == Calendar.TUESDAY
    }
}

This will get the job done (albeit in a cumbersome way), but it suffers from ProductHandler implementations not being hot-reloadable.

Creating a ProductHandler artefact

We can make things a bit easier and support hot reloading by creating an artefact for product handlers.

To do this we need a plugin…

class ProductHandlerGrailsPlugin {
    def version = "0.1"
    def grailsVersion = "1.2.* > *"
    
    // register the artefact handler
    def artefacts = [ProductHandlerArtefactHandler]
    
    // watch for any changes in these directories
    def watchedResources = [
        "file:./grails-app/productHandlers/**/*ProductHandler.groovy",
        "file:../../plugins/*/productHandlers/**/*ProductHandler.groovy"
    ]
    
    // Grails calls this when one of the files matching 'watchedResources' changes.
    // We have to actually swap in the new class when the source changes
    // There isn't anything special here, it's just boilerplate.
    def onChange = { event ->
        if (application.isArtefactOfType(ProductHandlerArtefactHandler.TYPE, event.source)) {
            def oldClass = application.getProductHandlerClass(event.source.name)
            application.addArtefact(ProductHandlerArtefactHandler.TYPE, event.source)

            // Reload subclasses
            application.productHandlerClasses.each {
                if (it.clazz != event.source && oldClass.clazz.isAssignableFrom(it.clazz)) {
                    def newClass = application.classLoader.reloadClass(it.clazz.name)
                    application.addArtefact(ProductHandlerArtefactHandler.TYPE, newClass)
                }
            }
        }
    }
}

We also need a artefact handler implementation (which has to be in Java due to legacy reasons)…

import org.codehaus.groovy.grails.commons.ArtefactHandlerAdapter;

public class ProductHandlerArtefactHandler extends ArtefactHandlerAdapter {

    // the name for these artefacts in the application
    static public final String TYPE = "ProductHandler"; 

    // the suffix of all product handler classes (i.e. how they are identified as product handlers)
    static public final String SUFFIX = "ProductHandler"; 
    
    public ProductHandlerArtefactHandler() {
        super(TYPE, ProductHandlerClass.class, DefaultProductHandlerClass.class, SUFFIX);
    }
}

And a ProductHandlerClass interface (again in Java)…

import org.codehaus.groovy.grails.commons.GrailsClass;

public interface ProductHandlerClass extends GrailsClass {}

And a DefaultProductHandlerClass implementation of that interface (again in Java)…

import org.codehaus.groovy.grails.commons.AbstractGrailsClass;
import org.codehaus.groovy.grails.commons.GrailsClassUtils;
 
public class DefaultProductHandlerClass extends AbstractGrailsClass implements ProductHandlerClass {

    public DefaultProductHandlerClass(Class clazz) {
        super(clazz, ProductHandlerArtefactHandler.SUFFIX);
    }
    
    // This method will get the static property 'key' on the underlying 
    // ProductHandler class that it represents.
    public String getKey() { 
        Object key = GrailsClassUtils.getStaticPropertyValue(getClazz(), "key");
        if (key == null) {
            return null;
        } else {
            return key.toString();
        }
    }
}

That’s a shotgun intro to the Grails Artefact API classes like GrailsClass and ArtefactHandlerAdapter just covering the stuff we need. Check out the linked to source for deeper detail.

Here is what we have done…

  1. Create a plugin that registers an artefact handler for product handlers
  2. Register for changes to source in grails-app/productHandlers in the app and plugins
  3. Provide an onChange handler that reloads product handler classes when they change
  4. Provide an ArtefactHandler implementation that specifies what a product handler class is
  5. Provide a GrailsClass subinterface for product handler classes
  6. Provide an implementation of that interface that returns the static property key on the underlying class (among other things)

Putting it together

Armed with our new artefact, we can now simplify our initial implementation…

// Our registry
import grails.spring.BeanBuilder
class ProductHandlerService {
    def grailsApplication // autowired
    
    protected createBeanBuilder() {
        new BeanBuilder(grailsApplication.mainContext, grailsApplication.classLoader)
    }
    
    protected instantiateHandler(handlerClass, Product product) {
        createBeanBuilder().with {
            beans {
                handler(handlerClass, product: product) {
                    // autowire the handler so it can use services etc.
                    it.autowire = true
                }
            }
            createApplicationContext().getBean('handler')
        }
    }
    
    def getAt(Product product) {
        def productHandlerClasses = grailsApplication.productHandlerClasses // a list of DefaultProductHandlerClass
        def handlerClass = productHandlerClasses.find { it.key == product.handlerKey } // find the one with the matching key
        instantiateHandler(handlerClass, product)
    }
}

// Example Handler (in grails-app/productHandlers dir)
class OnlyOnTuesdayProductHandler implements ProductHandler {
    static key = 'onlyOnTuesday'
    Product product
    boolean isCanBeSold() {
        new GregorianCalendar().get(Calendar.DAY_OF_WEEK) == Calendar.TUESDAY
    }
}

Being more conventional

Each of our product handler classes must specify a key, that products use to link themselves to a particular handler implementation. You could do away with this, and infer the key from the class name quite easily.

class ProductHandlerService {
    // …snip…
    
    def getAt(Product product) {
        def handlerClass = grailsApplication.getProductHandlerClass(product.handlerKey)
        instantiateHandler(handlerClass, product)
    }
}

The grailsApplication.getProductHandlerClass() method given the value "onlyOnTuesday" would return the OnlyOnTuesdayProductHandler class object.

In this particular case I personally wouldn’t do this as every time I renamed a product handler class I would have to update the products that use that handler in the DB, but other situations may not have this issue.

Summary

All product handler classes are now reloadable, which would obviously be a substantial productivity boost, without all that much work on our part. This approach could be used for any kind of concept or set of classes in your application that don’t fit into the standard controller, service, taglib etc. pattern.

The Artefact API can seem a bit confusing if you don’t take into consideration that this was the heart of Grails in the early days. It’s still there and is used by a great many plugins (e.g. grails-quartz) as well as being used extensively internally in Grails, but it doesn’t quite have the same role that it used to.

Hopefully this has shown you how you can, and why you might want to, use the artefact API internally in your applications.

Posted: Jun 1st, 2010 @ 10:42 pm

Tags: #software  #grails  

Comments

Forwarding/Chaining and models in Grails

I recently hit the case where I need to forward a request in Grails to a second action, but that action needed access to the model generated by the first (note that this is different to params). The Grails documentation doesn’t mention the ability to pass models when forwarding at all, but if you check the source code you can see that the capability is there.

What’s also missing is how to access the existing model in a forwarded to or chained to action. If you trace the code, you see that the model eventually gets passed to Spring’s [WebUtils.exposeRequestAttributes()](http://static.springsource.org/spring/docs/current/javadoc-api/org/springframework/web/util/WebUtils.html#exposeRequestAttributes(javax.servlet.ServletRequest, java.util.Map)) method, that simply makes each item in the model a request attribute. So in Grails you can access them via the request variable…

class MyController {
    
    def first = {
        forward(action: 'second', model: [thing: "thing"])
    }
    
    def second = {
        assert request.thing == "thing"
        // go forth and do stuff
    }
    
}

Why use model instead of params?

Params can only be strings where as you can pass any type via the model.

Posted: May 24th, 2010 @ 2:35 pm

Tags: #software  #grails  

Comments

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