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

Entries in roo add-ons (6)

Friday
May252012

Spock and Roo - more complex mocks

Given this method to test:

public boolean isInstalljQueryUICommandAvailable() {

  String jsLocation = pathResolver.getFocusedIdentifier(
      Path.SRC_MAIN_WEBAPP, "/js");
  if (projectOperations.isFocusedProjectAvailable()) {
    boolean isMissingjQueryUI = fileManager.findMatchingAntPath(
        jsLocation + "/jquery-ui-*.min.js").isEmpty();
    return !isInstalljQueryCommandAvailable() && isMissingjQueryUI;
  } else {
    return false;
  }
}

We have several challenges here:

  • We are calling the isInstalljQueryCommandAvailable() method from my prior post, so we need to mock the code in that invocation
  • We are going to call the same methods with different results
  • We have to mock a non-empty call to the findMatchingAntPath

To refer you to the prior method, here it is:

public boolean isInstalljQueryCommandAvailable() {
  String jsLocation = pathResolver.getFocusedIdentifier(
      Path.SRC_MAIN_WEBAPP, "/js");

  return fileManager.findMatchingAntPath(
      jsLocation + "**/jquery-1.*.min.js").isEmpty();
}

Setting up our test mocks

The test setup needs to take those conditions into account. First, we define our test method and fill in what we expect to happen:

def "isJqueryUIInstallAvailable called and happy path"() {

     setup:

     when:
     def result = operations.isInstalljQueryUICommandAvailable()

     then:
     result == true
}

The setup

Ok, now let's define our setup block. We'll need to return a simulated search result for our invocation of the pathResolver.findMatchingAntPath method in the jquery availability check - we want to state that we already have jQuery, but not jQuery UI, in our search path. So, let's create a simulated file details object first:

setup:
FileDetails fd = new FileDetails(new File("foo"), 234L);
SortedSet<FileDetails> fileDetailsSet = new TreeSet<FileDetails>()
fileDetailsSet.add(fd)

Now, we'll start by defining our mock conditions for something where the return value won't vary: the getFocusedIdentifier method of the pathResolver. Because we're just mocking it anyway, we don't really care what we return. This is true in both cases where it is called. So, we'll just return a junk value, but do it twice so that we can expect it to be called two times, and return the same result:

2* operations.pathResolver.getFocusedIdentifier(_, _) >> "foo"

So far, so good. Next, we'll mock a call to the isFocusedProjectAvailable() method of projectOperations. Hey, I noticed I do this in the UI setup, but not in the jQuery API setup, so I found a bug this way! So, it's two invocations, and I added the single invocation to the other test methods for the jQuery API setup too. Yay, team!:

2* operations.projectOperations.isFocusedProjectAvailable() >> true

Next, the more difficult one. I want to make sure we find the jQuery API JS file in the search, but NOT the jQueryUI API. In other words, I want to make sure we have jQuery but NOT jQueryUI, so that we can then allow the user to install jQueryUI.

2* operations.fileManager.findMatchingAntPath(_) >>>
                [new TreeSet<FileDetails>(), fileDetailsSet]

Wait, what? Ok, the syntax goes like this: we want two calls to the findMatchingAntPath method. The triple greater-than signs state that each invocation will return a different value. The first time, we'll return an empty TreeSet, which is the contract the method provides if no search result is found. The second time, we'll return a mocked set of file details, with our bogus one inside (we're then calling the one for jQuery, not jQueryUI, and we have to fake out that we have something.

The full test looks like this:

def "isJqueryUIInstallAvailable called and happy path"() {

     setup:
     FileDetails fd = new FileDetails(new File("foo"), 234L);
     SortedSet<FileDetails> fileDetailsSet = new TreeSet<FileDetails>()
     fileDetailsSet.add(fd)

     2* operations.pathResolver.getFocusedIdentifier(_, _) >> "foo"
     2* operations.projectOperations.isFocusedProjectAvailable() >> true
     2* operations.fileManager.findMatchingAntPath(_) >>>
             [new TreeSet<FileDetails>(), fileDetailsSet]

     when:
     def result = operations.isInstalljQueryUICommandAvailable()

     then:
     result == true
}
Thursday
May242012

Spock and Roo - Maven's conventions step in to mess with me

Ok, here's a cautionary tale.

I had everything working just fine in one project using Spock - on Jenkins builds I was getting code coverage working. It was great!

Hey, listen, keep this in mind:

src/test/java is NOT src/main/groovy! Now my jQuery project is starting to use code coverage - go ahead and view the report...

Oh, and one more thing: bind them to test-compile, not test. AAAHH!

:)

Ok, here's my maven build fragment for running the tests (I assume now that the file set is no longer needed...)


<plugin>
    <groupId>org.codehaus.gmaven</groupId>
    <artifactId>gmaven-plugin</artifactId>
    <version>1.4</version>
    <configuration>
        <providerSelection>1.8</providerSelection>
    </configuration>
    <executions>
        <execution>
			<id>test-run</id>
            <goals>
				<goal>generateTestStubs</goal>
                <goal>testCompile</goal>
            </goals>
			<phase>test-compile</phase>
			<configuration>
				<sources>								
						<fileSet>
							<directory>src/test/groovy</directory>
							<includes>
								<include>**/*.groovy</include>
							</includes>
						</fileSet>							
				</sources>
			</configuration>						
        </execution>
    </executions>
    <dependencies>
        <dependency>
            <groupId>org.codehaus.gmaven.runtime</groupId>
            <artifactId>gmaven-runtime-1.7</artifactId>
            <version>1.3</version>
            <exclusions>
                <exclusion>
                    <groupId>org.codehaus.groovy</groupId>
                    <artifactId>groovy-all</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
            <version>1.8.6</version>
        </dependency>
        <dependency>
            <groupId>org.spockframework</groupId>
            <artifactId>spock-core</artifactId>
            <version>0.6-groovy-1.8</version>
        </dependency>
        <dependency>
            <groupId>org.spockframework</groupId>
            <artifactId>spock-spring</artifactId>
            <version>0.6-groovy-1.8</version>
        </dependency>
    </dependencies>
</plugin>
Thursday
May242012

Spock and Roo = easier add-on testing, part 2

Moving on to some more interesting tests. Given this method:


public boolean isInstalljQueryCommandAvailable() {
    String jsLocation = pathResolver.getFocusedIdentifier(
        Path.SRC_MAIN_WEBAPP, "/js");

    return fileManager.findMatchingAntPath(
        jsLocation + "**/jquery-1.*.min.js").isEmpty();
  }

I want to use Spock to test it. The challenge is the somewhat more nested set of objects. My add-on extends the AbstractOperations class (to get the embedded fileManager), so I need to mock that, plus mock the path resolver I've mounted with @Reference in my add-on as well.

To set it up I do this:

class JqueryuiOperationsImplTest extends spock.lang.Specification {

    JqueryuiOperationsImpl operations;

    def setup() {
        operations = new JqueryuiOperationsImpl();

        operations.pathResolver = Mock(PathResolver);
        operations.fileManager = Mock(FileManager);
    }

Spock mocks are similar to EasyMock, in that we then detail our assertions of what should happen before the test runs. In fact, based on a really interesting thread I found this AM while banging my head against the wall (don't do that, it hurts), if you put any mocking assertions in the when: part of a Spock test, it moves them to the setup: block. Anyway, here is my set of assertions:

    def "isJqueryInstallAvailable called and happy path"() {

        setup:
        1* operations.pathResolver.getFocusedIdentifier(
                _, _) >> "src/main/webapp/js"

        1* operations.fileManager.findMatchingAntPath(
                _ as String) >> new TreeSet<FileDetails>()

I'm using Spock's matchers to eat the expressions - I don't really care what we pass to the getFocusedIdentifier or findMatchingAntPath methods, I just want them mocked and I want them to return values.

The >> is what tells us that we're stubbing the return output.

Here is the full test:

    def "isJqueryInstallAvailable called and happy path"() {
        setup:
        1* operations.pathResolver.getFocusedIdentifier(
                _, _) >> "src/main/webapp/js"

        1* operations.fileManager.findMatchingAntPath(
                _ as String) >> new TreeSet<FileDetails>()

        when:
        def result = operations.isInstalljQueryCommandAvailable();

        then:
        result == true
    }

Don't do what I did to get my head bruised. I originally wrote this:


1* operations.fileManager.findMatchingAntPath(
                _ as String).empty() >> false

I got my brain mixed up because I saw the line:

    return fileManager.findMatchingAntPath(
        jsLocation + "**/jquery-1.*.min.js").isEmpty();

And that's going to throw a tasty NullPointerException because you're mocking the return of the method in fileManager, not the return statement! Oh, bother.

Tuesday
May222012

Spock and Roo = easier add-on testing, part 1

Hey all...

I've been working on my add-ons. If you're reading this from my website, head over to the Silly Weasel menu and get the repository OBR to install my Spock add-on when trying this.

You are running Groovy in Roo? Shame on you!

Hey, before I was a Roo in Action author, I was a huge fan of Grails. I still am, because Groovy + your favorite APIs is so much more concise to program against than with stock Java. So, one of my backburner projects was getting Groovy to work on Roo.

Roo doesn't like Groovy (yet)

It turns out the reason is AspectJ. Right now, our projects are all compiling against the AspectJ API (version 1.2.x if memory serves). The problem is that Groovy's bytecode is not compatible with AspectJ, at least in terms of getting it to compile in the same Maven compiler step. I looked on the AspectJ timeline, and saw AspectJ 1.5 and 2.0 will support Groovy as a target for weaving. Coolness!

But that doesn't stop us from using it to test!

Yep, that's right. Turns out you can attach the Groovy compiler to your test phase, and it works. By the time your tests are compiling, the main classes have been compiled and weaved with that AspectJ goodness. So your ITD methods are there, and you can use any old testing tool you want to test 'em.

Note, I think you may have issues with my approach, if you are going to use the DataOnDemand testing framework, but so far, it seems like it works well.

My example for the evening - testing with mocks for Add-on Commands

Ok, here's a practical example. My jQueryUI add-on command class delegates the isAvailable and install commands to the Operations Implementation class, JqueryuiOperationsImpl. I wanted to start working up the test coverage for this add-on, because right now I am failing my hudson build due to a lack of test coverage.

So, here is a Groovy Spock specification, which I named JQueryuiCommandsTest.groovy and placed in src/test/groovy (after installing the maven script I'll reference below it). I've even created a Spock Roo add-on for you to use to set this up in your own projects.



package org.sillyweasel.addons.jqueryui

class JQueryuiCommandsTest extends spock.lang.Specification {

    JqueryuiCommands commands
    JqueryuiOperations operations

    def setup() {
        operations = Mock()
        commands = new JqueryuiCommands();
        commands.operations = operations;
    }

    def "when calling availability we defer to implementation" () {
        when:
        boolean result = commands.isApiInstallCommandAvailable()

        then:
        1*operations.isInstalljQueryCommandAvailable()
    }

    def "when calling ui availability we defer to implementation" () {
        when:
        boolean result = commands.isInstalljQueryUIAvailable()

        then:
        1*operations.isInstalljQueryUICommandAvailable()
    }

    def "when calling installjQueryApi we defer to jquery operations method" () {
        when:
        commands.installjQueryApi()

        then:
        1*operations.installjQueryApi();
    }

    def "when calling installjQueryUI we defer to jquery operations method"() {
        when:
        commands.installjQueryUIApi()

        then:
        1*operations.installjQueryUIApi();
    }


}

How did you get this to work?

I basically stole the configuration for Spock and Maven, and tweaked it a bit. Here are the relevant bits of my Maven build:

<dependency>
    <groupId>org.spockframework</groupId>
    <artifactId>spock-core</artifactId>
    <version>0.6-groovy-1.8</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.spockframework</groupId>
    <artifactId>spock-spring</artifactId>
    <version>0.6-groovy-1.8</version>
    <scope>test</scope>
</dependency>

...

<plugin>
   <groupId>org.codehaus.gmaven</groupId>
   <artifactId>gmaven-plugin</artifactId>
   <version>1.4</version>
   <configuration>
       <providerSelection>1.8</providerSelection>
       <source>src/test/groovy</source>
   </configuration>
   <executions>
       <execution>
           <goals>
               <goal>testCompile</goal>
           </goals>
       </execution>
   </executions>
   <dependencies>
       <dependency>
           <groupId>org.codehaus.gmaven.runtime</groupId>
           <artifactId>gmaven-runtime-1.7</artifactId>
           <version>1.3</version>
           <exclusions>
               <exclusion>
                   <groupId>org.codehaus.groovy</groupId>
                   <artifactId>groovy-all</artifactId>
               </exclusion>
           </exclusions>
       </dependency>
       <dependency>
           <groupId>org.codehaus.groovy</groupId>
           <artifactId>groovy-all</artifactId>
           <version>1.8.6</version>
       </dependency>
       <dependency>
           <groupId>org.spockframework</groupId>
           <artifactId>spock-core</artifactId>
           <version>0.6-groovy-1.8</version>
       </dependency>
       <dependency>
           <groupId>org.spockframework</groupId>
           <artifactId>spock-spring</artifactId>
           <version>0.6-groovy-1.8</version>
       </dependency>
   </dependencies>
</plugin>

Wednesday
May162012

Roo Add-On Development, Part the Second - Testing XML Configurations

In the last post, we discussed unit testing Roo add-on code. I feel this is just as vital as testing any other piece of Java code, considering that every time you run the command in the container you literally have to boot it, update the OSGi bundle, and then test. The feedback loop is too long to fix little, annoying bugs like not properly parsing an XML document.

Roo "Advanced add-ons" and Configuration

Let's assume we're not stellar coders. Let's even assume that we aren't the best XML developers. I'm shining a bright line at myself here.

With the CoffeeScript add-on, we want to manipulate the pom.xml file - something we don't need a container to do. Roo uses that good ole' built-in JAXP library (and Apache implementation of course). As Ben Alex would say, "stock standard Java." So, we should be able to easily unit test it.

The CoffeescriptOperationsImpl class - under test!

Last blog we showed you how to test the CoffeescriptCommands object, which delegates calls to the CoffeescriptOperations OSGi bean, which is implemented by the CoffeescriptOperationsImpl class. This is where the add-on's work is being done. So, let's test it.

Setting up the test class and Mockito

Like last time, we need to configure Mockito. We'll assume you've read up on that post and have installed the proper dependencies.

We need to test that our operations code works, and that it calls the proper Roo services. So, let's create our bean under test, and then mock the collaborator:

public class CoffeescriptOperationsImplTest {
  private CoffeescriptOperationsImpl coffeescriptOperations; 

  @Before
  public void setUp() {
    coffeescriptOperations = new CoffeescriptOperationsImpl();
    coffeescriptOperations.projectOperations = 
      Mockito.mock(ProjectOperations.class);
  }
  ...
 

Again, we manually create our class under test, and configure our mocks, in keeping with typical unit tests of components. I had to widen the visibility of the projectOperations reference to 'friendly' access - so that this class, which lives in the same package as the code under test, can see it and replace it with a mock.

h2. Reviewing our method under test - setup()

Let's look at our setup method:

  public void setup(String coffeeDir, 
                  String outputDirectory, boolean bare) {
    String moduleName = projectOperations.getFocusedModuleName();
    Element coffeePluginElement = getCoffeeScriptPluginElement();
    Document document = coffeePluginElement.getOwnerDocument();

    if (bare) {
      addTextElement(document, coffeePluginElement, "bare", "true");
    } else {
      addTextElement(document, coffeePluginElement, "bare", 
        COFFEE_DEFAULT_BARE_SETTING);
    }

    if (coffeeDir != null && coffeeDir.trim().length() > 0) {
      addTextElement(document, coffeePluginElement, "coffeeDir", coffeeDir);
    } else {
      addTextElement(document, coffeePluginElement, "coffeeDir", 
        COFFEE_DEFAULT_SRC_DIRECTORY);
    }

    if (outputDirectory != null && outputDirectory.trim().length() > 0) {
      addTextElement(document, coffeePluginElement, "coffeeOutputDirectory", outputDirectory);
    } else {
      addTextElement(document, coffeePluginElement, "coffeeOutputDirectory", 
        COFFEE_DEFAULT_OUTPUT_DIRECTORY);
    }

    projectOperations.addBuildPlugin(moduleName, new Plugin(coffeePluginElement));
  }

It's clear that we have a LOT of branches in this code, but that's because we're taking input from our command itself. I'll lie here, and tell you that I've written tests against all of these branches, but again, I said I'm lying - and in a further lie, I'll tell you that "I'm gonna get to it!" However, here's why lying doesn't help - I'm sure I have bugs in this code, and I really need to verify it all.

Oh, and I was thinking - I have a few private methods to help me keep the code organized and modular... Perhaps I should test those too but that leads the way of code smell... Interesting read BTW.

Reviewing the tasks in the method

Ok, the method does a few things:

1. Asks a helper method for the Configuration XML file as a basis for the Maven plugin.
2. Does a couple of gyrations so that we can maniuplate the plugin nodes with the DOM API - since Roo's Maven object model is essentially a thin wrapper around the XML API we have to think more in XML. This is something I'll be exploring in the future.
3. Sets the options the user passed in.
4. Adds the build plugin to the Maven build.

Ultimately, though, we need to see if:

1. Given a call to setup(), and the appropriate parameters,
2. Does the Plugin contain the proper information

Our test method for the setup process

Ok,

  @Test
  public void testSetupCoffeescript() {

    when(coffeescriptOperations.projectOperations
       .getFocusedProjectName()).thenReturn("foo");

    // a way for Mockito to grab passed input parameters for testing
    ArgumentCaptor<Plugin> pluginCaptor = 
       ArgumentCaptor.forClass(Plugin.class);

    // invoke our method
    coffeescriptOperations.setup("baz", "bar", false);

    // did we call addBuildPlugin? Also, notice we capture what the
    // method passed to the mocked projectOperations.addBuildPlugin method
    // for the plugin XML Element code
    verify(coffeescriptOperations.projectOperations)
       .addBuildPlugin(any(String.class), pluginCaptor.capture());

    // Since the plugin has been called and we've captured the method's 
    // second argument, we'll pluck it out and take a gander...
    Plugin coffeescriptPlugin = pluginCaptor.getValue();

    // make sure they passed something!
    assertNotNull(coffeescriptPlugin);

    // checks against the model
    Assert.assertEquals("false", coffeescriptPlugin.getConfiguration()
        .getConfiguration().getElementsByTagName("bare")
        .item(0).getTextContent());

    Assert.assertEquals("bar", coffeescriptPlugin.getConfiguration()
        .getConfiguration().getElementsByTagName("coffeeOutputDirectory")
        .item(0).getTextContent());

    Assert.assertEquals("baz", coffeescriptPlugin.getConfiguration()
        .getConfiguration().getElementsByTagName("coffeeDir")
        .item(0).getTextContent());
  }

Mockito's ArgumentCaptor

I guess this is really a testing tools article, rather than a Roo article.

The ArgumentCaptor API is really useful to see what the values were for a mock that was called by your class under test. This is a way to verify that we were passing in the right plugin configuration to our Roo projectManager, which, after all, we aren't testing. That's the Roo team's job!

Wrap-up

Looking at it from a distance, Roo is just a Java platform that generates, manipulates and configures applications. So it can really do anything. However, rather than testing by re-deploying 10 times, we can run a fast Junit test 10 times instead.

If you go to my Silly Weasel link at the top of the blog page, you'll see the OBR URL for getting my Coffeescript, jQuery and (soon) Site add-ons. You can browse my maven repository (the same URL without the repository.xml ending) and grab the source for anything I've released.

Please send me comments if you'd like to add to this discussion.

Best,

Ken