Search code examples
unit-testingtestingbddgradle

Guide for Testing Gradle Scripts


What are the best practices for testing Gradle Scripts?

I currently unit test my ant scripts with antunit, but I'm looking to migrate to Gradle. I can only find articles on testing Java code from Gradle or Groovy, but nothing on testing the Gradle tasks that I create or testing Groovy in general. Is there an equivalent of antunit for Gradle? Has anyone played with a BDD framework (like cucumber) to test Gradle scripts?

For instance, I currently have the following Ant Target

<target name="dist-bin" depends="build" description="creates a zip distribution of the current build">
    <zip destfile="build/TIBant-bin.zip">
        <zipfileset dir="src/ant" includes="**" />
        <zipfileset dir="test" includes="**" prefix="test" />
        <zipfileset dir="build" includes="TIBant.jar" />
        <zipfileset dir="build" includes="TIBant-*.html" />
        <zipfileset dir="build/examples/XMLtoProperties"
                    includes="XMLtoProperties.html"
                    prefix="examples/XMLtoProperties" />
        <zipfileset dir="lib" includes="**" prefix="lib" />
        <zipfileset dir="src/xslt" includes="**" excludes="test/**,userguide.xslt" prefix="lib/xslt" />
        <zipfileset dir="." includes="copyright.html,LICENSE.txt" />
        <zipfileset dir="examples"
                    includes="**"
                    excludes="**/build/**,**/config/default.properties"
                    prefix="examples" />
    </zip>
</target>

As you can imagine, it's pretty easy to break this when refactoring the project, so I have the following antunit test to check it.

<target name="test-dist-bin">
    <delete file="build/TIBant-bin.zip" />
    <au:assertFalse message="Bin dist still present">
        <available file="build/TIBant-bin.zip" />
    </au:assertFalse>
    <antcall target="dist-bin" />
    <au:assertTrue message="Bin dist not created">
        <available file="build/TIBant-bin.zip" />
    </au:assertTrue>
    <delete dir="build/testBinDist" />
    <au:assertFalse message="TestBinDist still present">
        <available file="build/testBinDist" />
    </au:assertFalse>
    <mkdir dir="build/testBinDist" />
    <unzip src="build/TIBant-bin.zip" dest="build/testBinDist" />
    <au:assertFalse message="config dir present">
        <available file="build/testBinDist/config/default.properties" />
    </au:assertFalse>
    <au:assertTrue message="Ant Macros missing">
        <available file="build/testBinDist/tibant.xml" />
    </au:assertTrue>
    <au:assertTrue message="Engine Stopper Jar missing">
        <available file="build/testBinDist/TIBant.jar" />
    </au:assertTrue>
    <au:assertTrue message="Ant-contrib-missing">
        <available file="build/testBinDist/lib/ant-contrib-1.0b3.jar" />
    </au:assertTrue>
    <au:assertTrue message="ant-unit missing">
        <available file="build/testBinDist/lib/ant-antunit-1.2.jar" />
    </au:assertTrue>
    <au:assertTrue message="Copyright missing">
        <available file="build/testBinDist/copyright.html" />
    </au:assertTrue>
    <au:assertTrue message="License missing">
        <available file="build/testBinDist/LICENSE.txt" />
    </au:assertTrue>
    <au:assertFalse message="Src present">
        <available file="build/testBinDist/src/java/org/windyroad/tibant/EngineStopper.jar" />
    </au:assertFalse>
    <au:assertTrue message="example missing">
        <available file="build/testBinDist/examples/SimpleProject/src/bw/example/Build/example.archive" />
    </au:assertTrue>
    <au:assertFalse message="example has build files">
        <available file="build/testBinDist/examples/SimpleProject/build/*" />
    </au:assertFalse>
    <au:assertFalse message="example has default config file">
        <available file="build/testBinDist/examples/SimpleProject/config/default.properties" />
    </au:assertFalse>
    <property name="doc.file"
              location="build/testBinDist/TIBant-User-Guide.html" />
    <au:assertTrue message="doc missing: ${doc.file}">
        <available file="${doc.file}" />
    </au:assertTrue>
    <au:assertTrue message="xslt missing">
        <available file="build/testBinDist/lib/xslt/configure-ear.xslt" />
    </au:assertTrue>
    <subant target="run-quick-tests">
        <fileset dir="build/testBinDist" includes="build.xml" />
    </subant>
</target>

which is called by the following snippet to produce a nice xml report

                <au:antunit failonerror="@{failonerror}">
                    <propertyset>
                        <propertyref name="config.filename" />
                    </propertyset>
                    <path>
                        <pathelement location="@{antunit}" />
                    </path>
                    <au:plainlistener logLevel="info" />
                    <au:xmllistener todir="build" loglevel="info" />
                </au:antunit>

I understand how to migrate dist-bin to gradle, but I'm not sure what's the right way to migrate test-dist-bin and the au:antunit call.


Solution

  • Gradle 3.x test Toolkit available! Please, check out userguide here: https://docs.gradle.org/current/userguide/test_kit.html

    The correctness of the logic can then be verified by asserting the following, potentially in combination:

    • The build's output;
    • The build's logging (i.e. console output);
    • The set of tasks executed by the build and their results (e.g. FAILED, UP-TO-DATE etc.).

    copy-pasted example:

    import org.gradle.testkit.runner.BuildResult;
    import org.gradle.testkit.runner.GradleRunner;
    import org.junit.Before;
    import org.junit.Rule;
    import org.junit.Test;
    import org.junit.rules.TemporaryFolder;
    
    import java.io.BufferedWriter;
    import java.io.File;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.util.Collections;
    
    import static org.junit.Assert.assertEquals;
    import static org.junit.Assert.assertTrue;
    
    import static org.gradle.testkit.runner.TaskOutcome.*;
    
    public class BuildLogicFunctionalTest {
        @Rule public final TemporaryFolder testProjectDir = new TemporaryFolder();
        private File buildFile;
    
        @Before
        public void setup() throws IOException {
            buildFile = testProjectDir.newFile("build.gradle");
        }
    
        @Test
        public void testHelloWorldTask() throws IOException {
            String buildFileContent = "task helloWorld {" +
                                      "    doLast {" +
                                      "        println 'Hello world!'" +
                                      "    }" +
                                      "}";
            writeFile(buildFile, buildFileContent);
    
            BuildResult result = GradleRunner.create()
                .withProjectDir(testProjectDir.getRoot())
                .withArguments("helloWorld")
                .build();
    
            assertTrue(result.getOutput().contains("Hello world!"));
            assertEquals(result.task(":helloWorld").getOutcome(), SUCCESS);
        }
    
        private void writeFile(File destination, String content) throws IOException {
            BufferedWriter output = null;
            try {
                output = new BufferedWriter(new FileWriter(destination));
                output.write(content);
            } finally {
                if (output != null) {
                    output.close();
                }
            }
        }
    }