Question

Scenario

After having written an integration test in in JUnit 5, that executes the compiled project.jar, I used to manually compile the project after modifications, then manually execute the integration test. However, it is part of an click once installation, which automates the project compilation and execution using Gradle. And to build the project.jar with Gradle whilst evaluating JUnit integration test can be seen as a chicken and egg dilema; I compile the project so that I can run the integration test so that I can compile the project.

Solution

And gradle allows a build command to be run without testing the Unit-tests: gradle build -x tests hence I can compile the project without tests, then compile the project again using: gradle build which evaluates the integration test with the previously compiled version.

However, I feel like it may defeat the purpose of testing if you compile and run an untested compilation of project.jar for an integration test, in order to compile a tested project.jar

Question

What is a better way/protocol to deal with the compilation-integration test interdependence?

Adressing Comments

The project structure consists of the Maven Standard Directory Layout supplemented with separate /src/integTest/java and /src/integTest/resources folders. The complete project directory tree consists of:

├───.gradle
│   ├───5.4
│   │   ├───executionHistory
│   │   ├───fileChanges
│   │   ├───fileContent
│   │   ├───fileHashes
│   │   ├───javaCompile
│   │   └───vcsMetadata-1
│   ├───5.5.1
│   │   ├───executionHistory
│   │   ├───fileChanges
│   │   ├───fileContent
│   │   ├───fileHashes
│   │   ├───javaCompile
│   │   └───vcsMetadata-1
│   ├───buildOutputCleanup
│   └───vcs-1
├───.settings
├───bin
│   ├───default
│   │   └───customsortserver
│   ├───main
│   │   └───customsortserver
│   └───test
│       └───customsortserver
├───build
│   ├───classes
│   │   └───java
│   │       └───main
│   ├───generated
│   │   └───sources
│   │       └───annotationProcessor
│   │           └───java
│   │               └───main
│   └───tmp
│       └───compileJava
├───gradle
│   └───wrapper
└───src
    ├───integTest
    │   ├───java
    │   │   └───customsortserver
    │   └───resources
    │       ├───originalData
    │       ├───testDataSets
    │       │   ├───backlog
    │       │   └───pending
    │       ├───testOutput
    │       ├───wslCommandScripts
    │       └───wslLaunchers
    ├───main
    │   ├───java
    │   │   └───customsortserver
    │   └───resources
    └───test
        ├───java
        │   └───customsortserver
        └───resources

Where the /src/integTest/resources/wslLaunchers contain a powershell script that executes the compiled .jar file half way in the integration test.

Was it helpful?

Solution

Gradle builds the jar file in the jar task, and runs the integration tests in a task that you've defined (and I've assumed is called integTest, but change as needed).

To make sure the jar file is available when integration tests run, we can add a task dependency. This will ensure that the jar task always runs before the integTest task. One possible syntax is:

integTest.dependsOn jar

Once we have the jar file, we need its location in order to use it. This is build/libs/[projectname].jar relative to the Gradle project root. You could hard-code that into your Powershell script, or it might be more elegant (and less fragile) to e.g. pass a system property through from Gradle which can then be picked up in your integration test class.


Proof of concept code: https://github.com/StevenEddies/test-use-jar-test

To prove that it uses the jar from the same Gradle run, first run ./gradlew clean, then double check no jar files exist in any subfolders, then run ./gradlew integTest and see that the integration tests (which look for the class file in the jar) pass.

This is the example build.gradle file for the proof of concept:

apply plugin: 'java'
apply plugin: 'eclipse'
group = 'uk.me.eddies'

repositories {
    jcenter()
}

dependencies {
    compile 'org.slf4j:slf4j-api:1.7.21'
    runtime 'org.slf4j:slf4j-simple:1.7.21'
    testCompile 'junit:junit:4.12'
    testCompile 'org.hamcrest:hamcrest-all:1.3'
}

sourceSets {
    integTest {
        java {
            compileClasspath += main.output
            runtimeClasspath += main.output
        }
    }
}

configurations {
    integTestCompile.extendsFrom testCompile
    integTestRuntime.extendsFrom testRuntime
}

task integTest(type:Test) {
    group = 'verification'
    testClassesDirs = project.sourceSets.integTest.output.classesDirs
    classpath = project.sourceSets.integTest.runtimeClasspath
    check.dependsOn it
    dependsOn jar
}
Licensed under: CC-BY-SA with attribution
scroll top