Chariot Training Classes

Training Courses

I run Chariot's training and mentoring services. We provide training in AngularJS, HTML5, Spring, Hibernate, Maven, Scala, and more.

Chariot Education Services

Technology

Chariot Emerging Tech

Learn about upcoming technologies and trends from my colleagues at Chariot Solutions.

Resources

Chariot Conferences

Podcasts

« STS 3.1.0.RELEASE - not loving Spock / Groovy for ADJT projects | Main | My Roo add-ons talk at SpringOne/2GX is approved »
Thursday
Oct042012

More Spock love - how I tested complex install scenarios

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.

PrintView Printer Friendly Version

EmailEmail Article to Friend

Reader Comments (2)

Nice ramblings lol :) This Roo thing seems a wee bit complex tho... like just another framework to learn. I've been looking around at all the different RADs out there... I'm really looking for a RAD that makes development super simple for me because I'm constantly creating web apps for clients. I looked at Spring Boot (it was difficult) and Spring Roo (it was dificult too). The traditional choice is Maven but Ima buck the system kind of guy. I've been fiddling around with one I found called Jigy Generator... it's super simple. It generates your Spring project with everything completely configured for you without having to learn anything additional. It also reverse engineers your database to create all your DAO's, domain objects and validators for you. Plus you have certain things that just work out of the box like login, authentication, file upload etc. Have you tried this one? I got it from getjigy.com

October 17, 2014 | Unregistered CommenterAndrew Jackson

No, I haven't seen that. The Roo add-ons particularly are tough to write as you're writing code in relatively undocumented APIs, but the base features such as controllers and ORM code are relatively easy to work with.

November 13, 2014 | Registered CommenterKen Rimple

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>