Ken Rimple heads Chariot Solutions' training and mentoring programs, and has developed and/or delivered courseware and seminars in a variety of technologies such as Maven, OSGi, Groovy, Grails and Spring. Throughout his career, Ken has always made it a priority to teach others what he has learned. Ken has served as the technical co-chair of both the Fall Forecast 2008 Cloud Computing Conference and the 2009 - 2012 Emerging Technologies for the Enterprise conferences. He hosts a popular podcast, the Chariot TechCast, and has led or participated in projects written in Java since Java 1.0.2. Ken taught the first Philadelphia-area Sun Introduction to Java course in the late 1990s. He is the co-author (along with Srini Penchikala) of Spring Roo in Action for Manning Publications. He is also an avid photographer and jazz drummer. Ken is a DZone MVB and is not an employee of DZone and has posted 35 posts at DZone. You can read more from them at their website. View Full User Profile

More Spock Love - How I Tested Complex Install Scenarios

10.15.2012
| 1957 views |
  • submit to reddit

I'm in love. Officially. With Spock.

Ok, we've only been hanging out for a little bit, here and there. But one of the things I'm doing is prepping for an updated talk on Spring Roo add-ons at SpringOne/2GX. Rather than repeat the same things again, I wanted to show some practical help for people writing add-ons, things like how to write good tests.

Also, I wanted to point out a few things that need more work, bring up some issues for the JIRA queue, so that we can improve Roo further after SpringOne. We'll get to that later in this article.

Testing add-on availability

Testing whether an add-on setup command is available to expose to the Roo shell can be a bit tricky. For example, in the Spock Roo add-on, I expect that the user's shell is:

  • running in a project context
  • the project has to contain two maven dependencies for spock-core and spock-spring
  • the project has to contain one dependency for the gmaven-plugin to run Spock tests

So, there are several scenarios to test. I started down the road to testing them all individually. Bad developer. First off, there is a sort of truth table here:

installedDependenciesinstalledPluginsexpectedResult
nonenonetrue
depa, depbnonetrue
depaplugintrue
baddepnonetrue
depa, depbpluginfalse
depabadversion, depbplugintrue

Given:

  • depa and depb = the correct two Maven dependencies,
  • baddep is a Maven dependency completely unrelated
  • depabadversion is the same dependency but a different version
  • plug-in is the gmaven-plugin from codehaus
  • expectedResult is whether or not we can go ahead and install the add-on (i.e. replace what is there)

As I started the third method I saw so much duplication I started to back off. First I removed whatever extra calls I was making to various methods (such as projectOperations.getDependencies()) which made it easier to test (and debug). Then, I pulled out the data table syntax.

Here is what I ended up with. Totally boss, BTW:

package org.sillyweasel.roo.addons.spock
import org.springframework.roo.project.Dependency
import org.springframework.roo.project.Plugin
import org.springframework.roo.project.ProjectOperations
import org.springframework.roo.project.maven.Pom
import spock.lang.Unroll

class SpockOperationsImplTest extends spock.lang.Specification {

  static def depa = 
    new Dependency("org.springframework", "spock-core", "0.6-groovy-1.8")
  static def depb = 
    new Dependency ("org.springframework", "spock-spring", "0.6-groovy-1.8")
  static def depabadversion =
    new Dependency("org.springframework", "spock-core", "0.6-groovy-1.0")
  static def baddep = 
    new Dependency ("org.springframework", "sbad", "0.6-groovy-1.8")
  static def plugin = 
    new Plugin("org.codehaus.gmaven", "gmaven-plugin", "1.4")


  @Unroll
  def "install check - #situation"() {
    given:
      def pom = Mock(Pom.class)
      def operations = new SpockOperationsImpl()
      def projectOperations = Mock(ProjectOperations.class)
      operations.projectOperations = projectOperations

    when:
      def commandAvailable = operations.isSetupCommandAvailable()

    then:
      1 * projectOperations.isFocusedProjectAvailable() >> true
      1 * projectOperations.getFocusedModule() >> pom
      1 * pom.getDependencies() >> deps
      pom.getBuildPlugins() >> plugins
      assert commandAvailable == res

    where:
    deps          | plugins | res   | situation
    []            | []      | true  | "no installedDependencies or installedPlugin"
    [ depa, depb] | []      | true  | "all installedDependencies...
                                      but no installedPlugin"
    [ depa ]      | [plugin]| true  | "some installedDependencies...
                                       and installedPlugin"
    [ baddep ]    | []      | true  | "a different dependency and no plugins"
    [ depa, depb ]| [plugin]| false | "all installedDependencies and installedPlugin"
    [ depabadversion, depb ] 
                  | [plugin] | true | "bad version of a plugin"
  }
}

What's even better...

See the method name at the top? It has an embedded hash mark, and uses the `situation` field, plus the `@Unroll` annotation, to turn the method into five separate test methods on the fly at test time. Niiiice!

Roo's challenges - addon testing

Why am I doing all of my testing in Groovy when Roo is a Java-based framework? Three reasons:

  • I want to get rolling on add-ons,
  • I need to understand the weaknesses of the add-on API so I can try to help improve it,
  • I need to make sure I test the most code for the least effort!

To the middle point: Roo is based on Maven and Spring. It manages a Maven `pom.xml` file, as well as Spring, Java and other artifacts.

Because the Maven files are managed by some Java wrapper classes, written for the purpose of generating Maven POMs, they are currently good enough - you can fill them via an Xml DOM, for example, but they aren't inherently testable.

For example, the Pom class can't be mocked, so I can't inject a fake set of dependencies or plugins. That is, unless I install two Maven test dependencies, both of which Spock told me to use as a helpful hint! They are: `cglib-nodep` for mocking interface-less classes, and `objenesis` for creating instances of classes as Mocks without no-arg constructors. To wit:

<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib-nodep</artifactId>
  <version>2.2.2</version>
  <scope>test</scope>
</dependency>

<dependency>
  <groupId>org.objenesis</groupId>
  <artifactId>objenesis</artifactId>
  <version>1.2</version>
  <scope>test</scope>
</dependency>

With that, I can use:

def pom = Mock(Pom.class)

See you at SpringOne/2GX

I'll be talking about this and other techniques at SpringOne/2GX in two weeks, and I'll post my presentation later as well. I hope to see some of you there.

 

 

 

 

 

Published at DZone with permission of Ken Rimple, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)