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
- 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'
- Add ‘jCenter()’ underneath ‘mavenCentral()’ in the repositiories list/section…
repositories { mavenCentral() jcenter() }
- 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
- 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 🙂
- 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 😀