Search code examples
testinggroovyjunit

Simple testrunner setup for Groovy


I have a Jenkins shared library that has a bunch of classes in it, implemented in groovy, in the src/company folder. I have tests covering these and can run them locally by just invoking each file on its own, eg:

groovy --classpath src test/ThingTest.groovy

However, now I'm looking for something like Python's nose or pytest that I can run from CI and will discover/invoke each test, and afterward generate a nice XML summary. I found JUnit and Spock, but both seem to require immediately committing to hundreds of lines of pom/gradle/whatever files— this feels like a lot of potential maintenance burden to take on for someone otherwise outside the Java ecosystem who just wants to conveniently run some tests on every commit— especially when I don't even need that buildsystem stuff for the Jenkins shared library stuff, just for testing it!

As a novice who wants to do better at testing the reusable components of my Jenkins pipelines, where is the best and simplest place to start here? To be clear, I'm explicitly trying not to reinvent the wheel, but I don't want to have to install a special IDE just to be able to edit the boilerplate files needed for this— my hope is that a Java ecosystem expert can look at these requirements and say "cool, use X and Y, here's how to get a bare minimum setup to get that going".

The tests currently look like this, but there aren't that many of them, so it would be easy to change them to something else if that's required:

import groovy.test.GroovyTestCase
import org.junit.Test

import myorg.jenkins.Thing


class ThingTest extends GroovyTestCase {
  @Test
  public void testThing() {
    // stuff followed by assertEquals
  }

Following the advice of @kriegaex I have a partly-working Gradle setup now that looks like this:

plugins {
    id 'groovy'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.apache.groovy:groovy:4.0.14'
    implementation 'org.apache.groovy:groovy-json:4.0.14'

    testImplementation 'org.apache.groovy:groovy-test:4.0.14'
}

sourceSets {
    main {
        groovy {
            srcDirs = ['src', 'vars']
        }
    }
    test {
        groovy {
            srcDirs = ['test']
        }
    }
}

However, I'm hitting some dependency issues on execution:

$ gradle test

> Task :compileGroovy FAILED
startup failed:
/home/mikepurvis/jenkins-pipeline-helper/src/clearpath/jenkins/CPRCommonFunctions.groovy: 2: unable to resolve class hudson.model.Hudson
 @ line 2, column 1.
   import hudson.model.Hudson;
   ^

/home/mikepurvis/jenkins-pipeline-helper/vars/abortBuilds.groovy: 1: unable to resolve class hudson.model.Result
 @ line 1, column 1.
   import hudson.model.Result
   ^

/home/mikepurvis/jenkins-pipeline-helper/vars/abortBuilds.groovy: 2: unable to resolve class hudson.model.Run
 @ line 2, column 1.
   import hudson.model.Run
   ^

/home/mikepurvis/jenkins-pipeline-helper/vars/abortBuilds.groovy: 3: unable to resolve class jenkins.model.CauseOfInterruption.UserInterruption
 @ line 3, column 1.
   import jenkins.model.CauseOfInterruption.UserInterruption
   ^
...

It looks like there are a few tools out there that try to mock the jenkins/pipeline APIs for testing purposes, but I briefly tried one and it didn't seem to make it anywhere. I also wasn't successful in just directly pulling stuff from com.cloudbees into the build either. For example "Jenkins Core" is listed here with a specific pasteable Gradle snippet:

https://mvnrepository.com/artifact/org.jenkins-ci.main/jenkins-core/2.439

However adding that to my build fails with a could-not-resolve:

* What went wrong:
Execution failed for task ':compileGroovy'.
> Could not resolve all files for configuration ':compileClasspath'.
   > Could not resolve org.jenkins-ci.main:jenkins-core:2.439.

Possibly it would be easier to tell Gradle not to bother trying to compile this as a project at all? That's unfortunate, though, given that it would be nice to have the additional validation of that even for the helper methods that don't yet have real tests.


Solution

  • Thanks for showing a sample test in your updated question. Actually, the test does not make any sense like that. The built-in GroovyTestCase is a JUnit 3 class, but @Test is from JUnit 4 (and there is yet another @Test with another package name in JUnit 5 Jupiter). So, you can just omit the @Test annotations altogether and rely on class and method naming. So, given a file src/test/groovy/ThingTest.groovy like this:

    import groovy.test.GroovyTestCase
    
    class ThingTest extends GroovyTestCase {
      void testPassing() {
        assert true
      }
    
      void testFailing() {
        assert false
      }
    }
    

    you can build and run it in Gradle like this:

    plugins {
      id 'groovy'
    }
    
    repositories {
      mavenCentral()
    }
    
    dependencies {
      implementation 'org.apache.groovy:groovy:4.0.14'
      testImplementation 'org.apache.groovy:groovy-test:4.0.14'
    }
    

    Then just run gradle test.

    To build and run the same in Maven with mvn test, use this POM (a bit more wordy, because Maven is not so "groovy" out of the box):

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
    
      <groupId>dev.aspectj</groupId>
      <artifactId>Maven_POJOTestsGroovy</artifactId>
      <version>1.0-SNAPSHOT</version>
    
      <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <groovy.version>4.0.14</groovy.version>
      </properties>
    
      <dependencies>
        <dependency>
          <groupId>org.apache.groovy</groupId>
          <artifactId>groovy</artifactId>
          <version>${groovy.version}</version>
        </dependency>
        <dependency>
          <groupId>org.apache.groovy</groupId>
          <artifactId>groovy-test</artifactId>
          <version>${groovy.version}</version>
          <scope>test</scope>
        </dependency>
      </dependencies>
    
      <build>
        <plugins>
          <plugin>
            <groupId>org.codehaus.gmavenplus</groupId>
            <artifactId>gmavenplus-plugin</artifactId>
            <version>1.13.1</version>
            <executions>
              <execution>
                <goals>
                  <goal>execute</goal>
                </goals>
              </execution>
            </executions>
            <dependencies>
              <dependency>
                <groupId>org.apache.groovy</groupId>
                <artifactId>groovy</artifactId>
                <version>${groovy.version}</version>
                <scope>runtime</scope>
              </dependency>
            </dependencies>
          </plugin>
        </plugins>
      </build>
    
    </project>
    

    Besides, this little POJO test in Java...

    public class ThingTest {
      public void testPassing() {
        assert true;
      }
    
      public void testFailing() {
        assert false;
      }
    }
    

    ... would need a smaller POM:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
    
      <groupId>dev.aspectj</groupId>
      <artifactId>Maven_POJOTests</artifactId>
      <version>1.0-SNAPSHOT</version>
    
      <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      </properties>
    
    </project>
    

    While the Groovy test indirectly depends on JUnit 3, this POJO test does not even need that.

    You see, you have many options without much setup effort. For Spock, it would not be much more both in Gradle and Maven. But if you are happy with your JUnit-3-style GroovyTest, maybe that really is all you need, even though you are losing a lot of testing comfort when not using Spock (ideally) or JUnit 5 (second choice).