Search code examples
unit-testinggrailsintegration-testingfunctional-testing

Grails test-app classpath


I'm trying to use test support classes within my tests. I want these classes to be available for all different test types.

My directory structure is as follows;

  • /test/functional
  • /test/integration
  • /test/unit
  • /test/support

I have test helper classes within the /test/support folder that I would like to be available to each of the different test types.

I'm using GGTS and I've added the support folder to the classpath. But whenever I run my integration tests running 'test-app' I get a compiler 'unable to resolve class mypackage.support.MyClass

When I run my unit tests from within GGTS the support classes are found and used. I presume this is because the integration tests run my app in its own JVM.

Is there any way of telling grails to include my support package when running any of my tests? I don't want my test support classes to be in my application source folders.


Solution

  • The reason that it works for your unit tests inside the IDE is that all source folders get compiled into one directory, and that is added to your classpath along with the jars GGTS picks up from the project dependencies. This is convenient but misleading, because it doesn't take into account that Grails uses different classpaths for run-app and each of the test phases, which you see when you run the integration tests. GGTS doesn't really run the tests; it runs the same grails test-app process that you do from the commandline, and captures its output and listens for build events so it can update its JUnit view.

    It's possible to add extra jar files to the classpath for tests because you can hook into an Ant event and add it to the classpath before the tests start. But the compilation process is a lot more involved and it looks like it would be rather ugly/hackish to get it working, and would likely be brittle and stop working in the future when the Grails implementation changes.

    Here are some specifics about why it'd be non-trivial. I was hoping that you could call GrailsProjectTestCompiler.compileTests() for your extra directory, but you need to compile it along with the test/unit directory for unit tests and the test/integration directory for integration tests, and the compiler (GrailsProjectTestCompiler) presumes that each test phase only needs to compile that one directory. That compiler uses Gant, and each test phase has its own Grailsc subclass (org.grails.test.compiler.GrailsTestCompiler and org.grails.test.compiler.GrailsIntegrationTestCompiler) registered as taskdefs. So it should be possible to subclass them and add logic to compile both the standard directory and the shared directory, and register those as replacements, but that requires also subclassing and reworking GrailsProjectTestRunner (which instantiates GrailsProjectTestCompiler), and hooking into an event to replace the projectTestRunner field in _GrailsTest.groovy with your custom one, and at this point my brain hurts and I don't want to think about this anymore :)

    So instead of all this, I'd put the code in src/groovy and src/java, but in test-specific packages that make it easy to exclude the compiled classes from your WAR files. You can do that with a grails.war.resources closure in BuildConfig.groovy, e.g.

    grails.war.resources = { stagingDir ->
       println '\nDeleting test classes\n'
       delete(verbose: true) {
          // adjust as needed to only delete test-specific classes
          fileset dir: stagingDir, includes: '**/test/**/*.class'
       }
       println '\nFinished deleting test classes\n'
    }