0

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.

  • 1
    Your tests are separate from your application aren't they? App in src/main/java and tests in src/test/java? I'm trying to picture how your build is set up to fit the question you pose. If that were the case, then the tests are executed as part of the build (but the tests remain separate). – Berin Loritsch Aug 17 '19 at 21:20
  • 1
    If you need an install step in between running building and running tests, then most teams will have a build server (look up CI/CD). That handles building the project on every push to the host repository, deploying the app, then running the tests. – Berin Loritsch Aug 17 '19 at 21:22
  • @BerinLoritsch with the separate CI/CD server, do you mean to imply that the consequences of compiling the project without tests during the first compilation in that server, are negligible as any negative consequences of that action occur in a "sandbox"/server? – a.t. Aug 18 '19 at 10:30
  • 1
    Can't test this right now but have you tried adding integTest.dependsOn jar to your build.gradle? (replace integTest with the name of your integration test task) That will make sure the jar is available at the point where your integration tests are run, you would then need to tweak your powershell script to run the jar from the correct location under the build folder somewhere – just me Aug 18 '19 at 11:14
  • @justme thank you. the dependsOn appears to be designed to tackle the interdependence between integration test that depend on the compilation result, and testing during/before compilation. I have not yet found a source that can confirm it also functions for the .jar that is being compiled itself, rather than a different .jar and have not yet been able to include a parameterized integration test in my build.gradle script. If it is verified, you can post it as an answer that I can accept. – a.t. Aug 18 '19 at 12:02
1

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
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.