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 spring-roo (25)

Sunday
Aug192012

Watch my SpringSource Roo add-ons video w/Srini Penchikala and SpringSource's Josh Long

Sunday
Jul082012

Spock's data tables are sweet!

I'm working with Spock again for updating my CoffeeScript plugin for Roo 1.2.3. There have been a few helpful additions to the add-on APIs (unless I missed them in 1.2.2...) and so my code is getting a tad simpler.

The coolest thing is that converting my tests to Spock, I had an easy way to do a truth table testing condition when I wanted to try out a number of scenarios. Check out this snippet, specifically the "where:" block and the values from the where block variables within the test conditions.

I've switched this test to the more formal given: when: then: syntax, and added the where: section for the data tables settings. I think I was using setup: instead of given: and I also didn't put my expectations in the then: section, which they evaluate in regardless. It's more readable this way.

Note, the version of the coffeescript add-on listed in the test is irrelevant, as the method called to fetch the plugin ignores it but the Plugin constructor requires a version.

@Unroll("evalto #evalto for project available #available, 
         packaging #packaging and pluginList #pluginList")
def "test isPluginInstalled with scenarios"() {
    given:
    def projectOperations = Mock(ProjectOperations.class)
    coffeescriptOperations.projectOperations = projectOperations
    def pom = Mock(Pom.class)

    when:
    def result = coffeescriptOperations.isPluginInstalled()

    then:
    coffeescriptOperations.projectOperations
            .isFocusedProjectAvailable() >> available
    coffeescriptOperations.projectOperations.getFocusedModule() >> pom
    pom.getPackaging() >> packaging
    pom.getBuildPluginsExcludingVersion(_) >> pluginList

    result == evalto

    where:
    pluginList | available | packaging  | evalto
    [] | false | "foo" | false
    [] | true  | "war" | false
    [] | true  | "pom" | false
    [new Plugin("com.theoryinpractise",
            "coffee-maven-plugin", "1.2.0") ] | true | "war" | true
    [new Plugin("com.theoryinpractise",
            "coffee-maven-plugin", "1.2.0")] | true | "pom" | false
}

I was able to fold five specific test cases checking whether the project contains the Coffeescript plugin into one test with five data settings (all false except one):

  • The focused project is not available
  • The packaging is a war, the focused project is available, the add-on is not installed
  • The packaging is a "pom" and the project is available
  • The plugin exists, the type is a war, the focused module is available (happy!)
  • The plugin exists (mounted by hand perhaps) on a pom project which is the current module

I used @Unroll to turn the data table results into five separate tests, each of which listed separately in the test results. Note the method name of the test is comprised of literal strings with data from the datatable columns starting with '#'. Nice, eh?

So I was able to collapse five test scenarios into one with the data tables. Nice.

You can pull the git repository for this add-on by using:

git clone git://git.cloudbees.com/sillyweasel/coffeescript-roo-addon.git
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.