Search code examples
jenkinstestinggradledeploymentsystem

how to implement gradle system test that get executed after a jenkins deployment?


I have a gradle project that is built by Jenkins within the following steps:

  1. Build the project, run unit- and integrationtests
  2. Build and publish docker images
  3. Deploy application to pre-production
  4. Run system tests on pre-production
  5. Deploy application to production

Now step 4 is not ready yet and I am wondering about how to implement this in the most convenient way..?

I want to use JUnit5 and the tests should be inside the project itself, so either in one of the modules or with a separate module that is also included and has some other modules as dependencies.

When I create a separate module with normal JUnit5 tests for it, these Tests get automatically executed in Step 1. So what I want to achieve is a custom gradle step for these system tests.


Solution

  • A good way to do so would be to implement a new gradle task and a new source set with the same name - for example "systemTest". Here is the sample code for a multi module gradle project that has a simple system test:

    my file hierarchy:

    ├── application
    │   ├── build.gradle
    │   └── src
    │       ├── main
    │       │   └── java
    │       │       └── de
    │       │           └── mycompany
    │       │               └── mydepartment
    │       │                   └── projectname
    │       │                       └── Application.java
    │       └── test
    │           └── java
    │               └── de
    │                   └── mycompany
    │                       └── mydepartment
    │                           └── projectname
    │                               └── ApplicationTest.java
    ├── build.gradle
    ├── gradle
    │   └── wrapper
    │       ├── gradle-wrapper.jar
    │       └── gradle-wrapper.properties
    ├── gradlew
    ├── gradlew.bat
    ├── Jenkinsfile
    ├── settings.gradle
    └── systemtests
        ├── build.gradle
        └── src
            └── systemTest
                └── java
                    └── de
                        └── mycompany
                            └── mydepartment
                                └── projectname
                                    └── SystemTest.java
    

    Application.java contains my business logic (that should shomehow deploy an application to docker or whatever).

    ApplicationTest.java is a JUnit5 unit test that will be run during the build process.

    SystemTest.java is a JUnit5 test that is intended to be run after a deployment. We can do this (manually or automatically if the command is added into a build script) via ./gradlew systemTest.

    Now let's look at the gradle files:

    build.gradle (root):

    subprojects {
        apply plugin: 'java'
    
        repositories {
            maven {
                name 'Nexus-Internal Access'
                url 'http://m2repo.system.local/content/groups/full/'
            }
        }
    
        dependencies {
            testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.6.0'
            testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.6.0'
        }
    
        sourceSets {
            main {
                java { output.resourcesDir = outputDir }
            }
            test {
                java { output.resourcesDir = outputDir }
            }
        }
    
        test {
            useJUnitPlatform()
        }
    }
    

    settings.gradle (root):

    rootProject.name = 'service'
    
    include 'application'
    include 'systemtests'
    

    application/build.gradle:

    apply plugin: 'application'
    
    application {
        mainClassName = 'de.mycompany.mydepartment.projectname.Application'
    }
    

    systemtests/build.gradle:

    configurations {
        systemTestImplementation.extendsFrom testImplementation
    }
    
    sourceSets {
        systemTest {
            java { output.resourcesDir = outputDir }
        }
    
        jar.duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    }
    
    task systemTest(type: Test) {
        useJUnitPlatform()
        group = 'verification'
        testClassesDirs = sourceSets.systemTest.output.classesDirs
        classpath = sourceSets.systemTest.runtimeClasspath
    }
    

    Jenkinsfile (untested phrases for your inspiration):

    #!groovy
    
    node {
        stage("Build") {
            sh """
                ./gradlew clean build
            """
        }
        stage("Build docker containers") {
            sh """
                docker build application
            """
        }
        stage("Deploy to pre-prod") {
            sh """
                oc rollout application to testserver
            """
        }
        stage("System Tests") {
            sh """
                ./gradlew clean systemTest --info
            """
        }
        stage("Deploy to prod") {
            sh """
                oc rollout application to prod
            """
        }
    }
    

    Note that I use --info on the /gradlew systemTest task so that I can read logs coming from the system test in the Jenkins Console. I am also using system properties to pass some credentials from our Jenkins into gradle. with another -Pname=${var} parameter for the systemTest task. Omitted here so that nobody gets confused :)