Introduction

A JAR is a package file format typically used to aggregate many Java class files and associated metadata and resources into one file for distribution. It is used to store classes of java created by the user in order to help the runnable and inference concepts embedded within the language.

An uber-JAR—also known as a fat JAR or JAR with dependencies—is a JAR file that contains not only a Java program, but embeds its dependencies as well. This means that the JAR functions as an “all-in-one” distribution of the software, without needing any other Java code.

In this section, we will be creating a fat JAR (one that contains all test classes too, as our project sits within ‘test’), which we can then easily use and distribute (e.g. on build servers).

To do this, we will be using the handy Gradle plugin Shadow.

Adding Shadow Plugin

  1. Open up the build.gradle file and add the following plugins underneath the ‘apply plugin: java’ line…
    apply plugin: 'application'
    apply plugin: 'com.github.johnrengelman.shadow'
  2. Add ‘jCenter()’ underneath ‘mavenCentral()’ in the repositiories list/section…
    repositories { 
        mavenCentral() 
        jcenter() 
    }
  3. Also add the following build scripts ‘repositories’ section to look like below, as we need to add a new repository source ‘jcenter’ as well as the classpath for the Shadow plugin…
    buildscript {
        repositories {
            mavenCentral()
            jcenter()
        }
        dependencies {
            classpath 'com.github.jengelman.gradle.plugins:shadow:5.1.0'
        }
    }

    If you get an error about Shadow plugin only being supported with Gradle v5.0+, then simply update the version in your gradle-wrapper.properties file (located at <projectRoot>/gradle/wrapper directory)…

    #Sun Sep 22 14:56:19 BST 2019
    distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
    distributionBase=GRADLE_USER_HOME
    distributionPath=wrapper/dists
    zipStorePath=wrapper/dists
    zipStoreBase=GRADLE_USER_HOME
  4. In its current form, the whole build.gradle file should look like below…
    group = 'com.testifyqagradleframework'
    version = '1.0-SNAPSHOT'
    
    apply plugin: 'java'
    apply plugin: 'com.github.johnrengelman.shadow'
    
    compileJava {
        sourceCompatibility = 1.8
        targetCompatibility = 1.8
    }
    
    repositories { 
        mavenCentral() 
        jcenter() 
    }
    
    buildscript {
        repositories {
            mavenCentral()
            jcenter()
        }
        dependencies {
            classpath 'com.github.jengelman.gradle.plugins:shadow:5.1.0'
        }
    }
    
    dependencies {
        compile group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '3.141.59'
        compile group: 'org.apache.logging.log4j', name: 'log4j-core', version:'2.11.1'
        compile group: 'org.apache.logging.log4j', name: 'log4j-api', version:'2.11.1'
        compile group: 'com.google.guava', name: 'guava', version:'25.1-jre'
        compile group: 'com.google.code.gson', name: 'gson', version:'2.8.5'
        compile group: 'io.github.bonigarcia', name: 'webdrivermanager', version: '3.3.0'
        compile group: 'org.json', name: 'json', version:'20180130'
        testCompile group: 'io.github.prashant-ramcharan', name: 'courgette-jvm', version: '3.0.1'
    }
    
    tasks.withType(Test) {
        systemProperties = System.getProperties()
        systemProperties.remove("java.endorsed.dirs")
    }
    
    task runTests(type: Test) {
        useTestNG() // specify that we use TestNG instead of JUnit
        include '**/TestRunner.class'
        outputs.upToDateWhen { false }
    }

Adding shadowJar Gradle Task

Let’s now add a Gradle task that when run, will create the fat JAR for us 🙂

  1. In the build.gradle file, add the following task, changing ‘whateverYouWant’ to a name appropriate for your project…
    shadowJar {
        mainClassName = 'seleniumgradle'
        baseName = 'seleniumgradle-test'
        classifier = 'tests'
        from sourceSets.test.output
        configurations = [project.configurations.testRuntime]
    }

    The whole thing should look like below…

    group = 'com.testifyqagradleframework'
    version = '1.0-SNAPSHOT'
    
    apply plugin: 'java'
    apply plugin: 'application'
    apply plugin: 'com.github.johnrengelman.shadow'
    
    compileJava {
        sourceCompatibility = 11
        targetCompatibility = 11
    }
    
    repositories {
        mavenCentral()
        jcenter()
    }
    
    buildscript {
        repositories {
            mavenCentral()
            jcenter()
        }
        dependencies {
            classpath 'com.github.jengelman.gradle.plugins:shadow:5.1.0'
        }
    }
    
    dependencies {
        compile group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '3.141.59'
        compile group: 'org.apache.logging.log4j', name: 'log4j-core', version:'2.11.1'
        compile group: 'org.apache.logging.log4j', name: 'log4j-api', version:'2.11.1'
        compile group: 'com.google.guava', name: 'guava', version:'25.1-jre'
        compile group: 'com.google.code.gson', name: 'gson', version:'2.8.5'
        compile group: 'io.github.bonigarcia', name: 'webdrivermanager', version: '3.3.0'
        compile group: 'org.json', name: 'json', version:'20180130'
        testCompile group: 'io.github.prashant-ramcharan', name: 'courgette-jvm', version: '3.0.1'
    }
    
    shadowJar {
        mainClassName = 'seleniumgradle'
        baseName = 'seleniumgradle-test'
        classifier = 'tests'
        from sourceSets.test.output
        configurations = [project.configurations.testRuntime]
    }
    
    tasks.withType(Test) {
        systemProperties = System.getProperties()
        systemProperties.remove("java.endorsed.dirs")
    }
    
    task runTests(type: Test) {
        useTestNG() // specify that we use TestNG instead of JUnit
        include '**/TestRunner.class'
        outputs.upToDateWhen { false }
    }

We can now build our JAR file by opening a Terminal window and entering the command:

./gradlew shadowJaR

The JAR file will be built by default into <projectRoot>/build/libs directory

In the next section, we will move towards scaling up our parallel test execution with a Selenium Grid inside docker containers 😀

Liked it? Take a second to support Thomas on Patreon!

Previous Article

Next Article

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.