<!--
  ~ Copyright 2015 the original author or authors.
  ~
  ~ Licensed under the Apache License, Version 2.0 (the "License");
  ~ you may not use this file except in compliance with the License.
  ~ You may obtain a copy of the License at
  ~
  ~      http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing, software
  ~ distributed under the License is distributed on an "AS IS" BASIS,
  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License.
  -->

<chapter id="test_kit">
    <title>The Gradle TestKit</title>
    <note>
        <para>
            The Gradle TestKit is currently <link linkend="feature_lifecycle">incubating</link>.
            Please be aware that its API and other characteristics may change in later Gradle versions.
        </para>
    </note>
    <para>
        The Gradle TestKit (a.k.a. just TestKit) is a library that aids in testing Gradle plugins and build logic generally.
        At this time, it is focused on
        <emphasis>functional</emphasis>
        testing.
        That is, testing build logic by exercising it as part of a programmatically executed build.
        Over time, the TestKit will likely expand to facilitate other kinds of tests.
    </para>

    <section>
        <title>Usage</title>
        <para>To use the TestKit, include the following in your plugin's build:</para>
        <sample id="testKitDependency" dir="testKit/testKitJunit" title="Declaring the TestKit dependency">
            <sourcefile file="build.gradle" snippet="declare-gradle-testkit-dependency"/>
        </sample>
        <para>
            The
            <literal>gradleTestKit()</literal>
            encompasses the classes of the TestKit, as well as the <link linkend="embedding">Gradle Tooling API client</link>.
            It does not include a version of<ulink url="http://junit.org">JUnit</ulink>,<ulink url="http://testng.org">TestNG</ulink>, or any other test execution framework.
            Such a dependency must be explicitly declared.
        </para>
        <sample id="junitDependency" dir="testKit/testKitJunit" title="Declaring the JUnit dependency">
            <sourcefile file="build.gradle" snippet="declare-junit-dependency"/>
        </sample>
    </section>

    <section>
        <title>Functionally testing with the Gradle runner</title>
        <para>
            The <apilink class="org.gradle.testkit.runner.GradleRunner"/> facilitates programmatically executing Gradle builds, and inspecting the result.
        </para>
        <para>
            A contrived build can be created (e.g. programmatically, or from a template) that exercises the “logic under test”.
            The build can then be executed, potentially in a variety of ways (e.g. different combinations of tasks and arguments).
            The correctness of the logic can then be verified by asserting the following, potentially in combination:
            <itemizedlist>
                <listitem>The build's output;</listitem>
                <listitem>The build's logging (i.e. console output);</listitem>
                <listitem>The set of tasks executed by the build and their results (e.g. FAILED, UP-TO-DATE etc.).</listitem>
            </itemizedlist>
            After creating and configuring a runner instance, the build can be executed via the
            <apilink class="org.gradle.testkit.runner.GradleRunner" method="build"/>
            or
            <apilink class="org.gradle.testkit.runner.GradleRunner" method="buildAndFail"/>
            methods depending on the anticipated outcome.
        </para>
        <para>
            The following demonstrates the usage of Gradle runner in a Java JUnit test:
        </para>
        <sample id="testKitFunctionalTestJunit" dir="testKit/testKitJunit/src/test/java/org/gradle/sample" title="Using GradleRunner with JUnit">
            <sourcefile file="BuildLogicFunctionalTest.java" snippet="functional-test-junit"/>
        </sample>
        <para>
            Any test execution framework can be used.
        </para>
        <para>
            As Gradle build scripts are written in the Groovy programming language, and as many plugins are implemented in Groovy,
            it is often a productive choice to write Gradle functional tests in Groovy.
            Furthermore, it is recommended to use the (Groovy based) <ulink url="https://code.google.com/p/spock/">Spock test execution framework</ulink>
            as it offers many compelling features over the use of JUnit.
        </para>
        <para>
            The following demonstrates the usage of Gradle runner in a Groovy Spock test:
        </para>
        <sample id="testKitFunctionalTestSpock" dir="testKit/testKitSpock/src/test/groovy/org/gradle/sample" title="Using GradleRunner with Spock">
            <sourcefile file="BuildLogicFunctionalTest.groovy" snippet="functional-test-spock"/>
        </sample>
        <para>
            It is a common practice to implement any custom build logic (like plugins and task types) that is more complex in nature as external classes in a standalone project. The main
            driver behind this approach is bundle the compiled code into a JAR file, publish it to a binary repository and reuse it across various projects.
        </para>
        <section>
            <title>Getting the code under test into the test build</title>
            <para>
                The runner uses the <link linkend="embedding">Tooling API</link> to execute builds.
                An implication of this is that the builds are executed in a separate process (i.e. not the same process executing the tests).
                Therefore, the test build does not share the same classpath or classloaders as the test process and the code under test is not implicitly available to the test build.
            </para>
            <para>
                At the moment the TestKit does not provide any convenient mechanism to inject the code under test into the test builds.
                This feature will be added to future versions.
            </para>
            <para>
                In the meantime, it is possible to manually make the code under test available via some extra configuration.
                The following example demonstrates having the build generate a file denoting the implementation classpath of the code under test, and making it available at test runtime.
            </para>
            <sample id="testKitFunctionalTestSpockClassesBuildConfig" dir="testKit/testKitSpockClasspath" includeLocation="true" title="Making the code under test classpath available to the tests">
                <sourcefile file="plugin/build.gradle" snippet="test-logic-classpath"/>
            </sample>
            <para>
                The tests can then read this value, and inject the classpath into the test build.
                The following is an example (in Groovy) of doing this from within a Spock Framework <literal>setup()</literal> method, which is analogous to a JUnit <literal>@Before</literal> method.
            </para>
            <sample id="testKitFunctionalTestSpockClassesTestConfig" dir="testKit/testKitSpockClasspath" includeLocation="true" title="Injecting the code under test classes into test builds">
                <sourcefile file="plugin/src/test/groovy/org/gradle/sample/BuildLogicFunctionalTest.groovy" snippet="functional-test-classpath-setup"/>
            </sample>
            <para>
                This approach works well when executing the functional tests as part of the Gradle build.
                When executing the functional tests from an IDE, there are extra considerations.
                Namely, the classpath manifest file points to the class files etc. generated by Gradle and not the IDE.
                This means that after making a change to the source of the code under test, the source must be recompiled by Gradle.
                Similarly, if the effective classpath of the code under test changes, the manifest must be regenerated.
                In either case, executing the <literal>testClasses</literal> task of the build will ensure that things are up to date.
            </para>
        </section>
        <section>
            <title>Controlling the build environment</title>
            <para>
                The runner does not currently attempt to execute the test builds in an isolated environment.
                It also does not expose mechanism for fine grained control of environment variables etc.
                Future versions of the TestKit will provide better isolation and control.
            </para>
            <para>
                Unless a specific Gradle user home dir is specified via the build arguments (i.e. <userinput>-g</userinput> or <userinput>--gradle-user-home</userinput>),
                Gradle will use its default of <literal>~/.gradle</literal>.
                This may not be desirable.
                Any environmental configuration (e.g. <literal>~/.gradle/gradle.properties</literal>) for the current user will be inherited.
                You may wish to isolate test builds by specifying an explicit Gradle user home to use via the build arguments.
            </para>
        </section>
        <section>
            <title>The Gradle version used to test</title>
            <para>
                The Gradle runner requires a Gradle distribution in order to execute the build.
                The TestKit does not depend on all of Gradle's implementation.
            </para>
            <para>
                When a runner is created, it will attempt to find a Gradle distribution based on where the <literal>GradleRunner</literal> class was loaded from.
                That is, it is expected that the class was loaded from a Gradle distribution, as is the case when using the <literal>gradleTestKit()</literal> dependency declaration.
            </para>
            <para>
                When using the runner as part of tests <emphasis>being executed by Gradle</emphasis> (e.g. executing the <literal>test</literal> task of a plugin project), the same distribution used to execute the tests will be used by the runner.
                When using the runner as part of tests <emphasis>being executed by an IDE</emphasis>, the same distribution of Gradle that was used when importing the project will be used.
                This means that the plugin will effectively be tested with the same version of Gradle that it is being built with.
            </para>
            <para>
                If a Gradle distribution cannot be found, creation of the runner instance will fail.
            </para>
            <para>
                Future versions of the TestKit will support more powerful distribution discovery, facilitating cross version testing.
            </para>
        </section>
        <section>
            <title>Debugging build logic</title>
            <para>
                The runner uses the <link linkend="embedding">Tooling API</link> to execute builds.
                An implication of this is that the builds are executed in a separate process (i.e. not the same process executing the tests).
                Therefore, executing your <emphasis>tests</emphasis> in debug mode does not allow you to debug your build logic as you may expect.
                Any breakpoints set in your IDE will be not be tripped by the code being exercised by the test build.
            </para>
            <para>
                At this time, there is no way to effectively debug the build logic under test.
                This, highly desirable, feature will be available in future versions of the test kit.
            </para>
        </section>
    </section>
</chapter>
