06 · Java Setup for Testers¶
From here on, every module runs real code. This one gets your machine ready: a JDK, an IDE, and — most importantly for a tester — Maven, the build tool that downloads JUnit, Selenium and TestNG for you and runs your test suite from a single command.
1. Install the JDK¶
You need a JDK (Java Development Kit — compiler + runtime), not just a JRE. Java 17 or 21 (both Long-Term Support releases) is the right choice for new test projects.
- macOS:
brew install openjdk@21, or download from adoptium.net. - Windows: download the Temurin 21 MSI installer from
adoptium.net and let it set
JAVA_HOME. - Linux:
sudo apt install openjdk-21-jdk(Debian/Ubuntu).
Verify:
openjdk version "21.0.3" 2024-04-16 LTS
OpenJDK Runtime Environment Temurin-21.0.3+9 (build 21.0.3+9-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.3+9 (build 21.0.3+9-LTS, mixed mode)
javac 21.0.3
/Library/Java/JavaVirtualMachines/temurin-21.jdk/Contents/Home
If JAVA_HOME is empty
Maven and many test tools read JAVA_HOME, not your PATH. On
macOS/Linux add to ~/.zshrc or ~/.bashrc:
export JAVA_HOME=$(/usr/libexec/java_home -v 21) (macOS) or
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 (Linux). On
Windows set it in System Properties → Environment Variables. Restart
your terminal afterwards.
2. Install an IDE¶
IntelliJ IDEA Community Edition (free) is the standard for Java test automation — it has the best JUnit/TestNG integration. Eclipse and VS Code with the Extension Pack for Java also work.
The IntelliJ features you will use constantly as a tester:
| Feature | Shortcut (macOS / Windows) |
|---|---|
| Run the test under the cursor | ⌃⇧R / Ctrl+Shift+F10 |
| Re-run the last thing | ⌃R / Shift+F10 |
| Go to declaration | ⌘B / Ctrl+B |
| Find usages | ⌥F7 / Alt+F7 |
| Reformat code | ⌥⌘L / Ctrl+Alt+L |
| Search everywhere | Shift Shift |
| Toggle breakpoint | ⌘F8 / Ctrl+F8 |
3. Why testers need Maven¶
Selenium alone pulls in a dozen JAR files, each with its own dependencies. Downloading them by hand and putting them on the classpath is unmanageable. Maven solves three problems at once:
- Dependency management — you declare "I want Selenium 4.23.0" and it fetches that plus everything Selenium needs.
- Standard project layout — every Maven test project looks the same, so anyone can navigate yours.
- Lifecycle commands —
mvn testcompiles and runs your whole suite, identically on your laptop and on a CI server (Level 3, Module 03).
Install it:
- macOS:
brew install maven - Windows: download the binary zip from
maven.apache.org, unzip, add the
binfolder toPATH. - Linux:
sudo apt install maven
Apache Maven 3.9.8
Maven home: /opt/homebrew/Cellar/maven/3.9.8/libexec
Java version: 21.0.3, vendor: Eclipse Adoptium
Default locale: en_IN, platform encoding: UTF-8
OS name: "mac os x", version: "14.5", arch: "aarch64"
4. The standard Maven project layout¶
java-testing-practice/
├── pom.xml ← the project definition
└── src
├── main
│ ├── java/ ← application code (often empty in a QA project)
│ └── resources/
└── test
├── java/ ← ALL your test classes live here
│ └── com/example/tests/
│ └── CalculatorTest.java
└── resources/ ← test data: config.properties, testng.xml, CSV files
The single most important rule: test classes go in src/test/java. Maven
only runs tests found there, and code in src/test is excluded from the
shipped artifact. Put a test in src/main/java and mvn test will silently
ignore it.
5. Create the project¶
mkdir -p java-testing-practice/src/test/java/com/example/tests
mkdir -p java-testing-practice/src/main/java
mkdir -p java-testing-practice/src/test/resources
cd java-testing-practice
Now the pom.xml — the file that defines the whole project. This one carries
everything Modules 07–10 need:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>java-testing-practice</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- JUnit 5 -- Module 07 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
<!-- Selenium WebDriver -- Module 08 -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.23.0</version>
<scope>test</scope>
</dependency>
<!-- TestNG -- Module 09 -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.10.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Surefire runs the test suite during `mvn test` -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
</plugin>
</plugins>
</build>
</project>
Reading a dependency¶
<dependency>
<groupId>org.junit.jupiter</groupId> <!-- who publishes it -->
<artifactId>junit-jupiter</artifactId> <!-- which library -->
<version>5.10.2</version> <!-- which release -->
<scope>test</scope> <!-- only on the test classpath -->
</dependency>
Those three coordinates — groupId, artifactId, version — uniquely identify
any library on Maven Central. <scope>test</scope> means the JAR is
available to src/test/java but never bundled into the application; every
testing library should use it.
Pull everything down:
[INFO] --- dependency:3.6.1:resolve (default-cli) @ java-testing-practice ---
[INFO] The following files have been resolved:
[INFO] org.junit.jupiter:junit-jupiter:jar:5.10.2:test
[INFO] org.seleniumhq.selenium:selenium-java:jar:4.23.0:test
[INFO] org.testng:testng:jar:7.10.2:test
[INFO] BUILD SUCCESS
6. Java you actually need as a tester¶
You do not need to be a Java developer to automate tests, but these six things appear in every test you will ever write.
// 1. A class -- every test lives inside one
public class Demo {
// 2. A method -- one test = one method
public static void main(String[] args) {
// 3. Variables and types
String username = "qa.user@example.com";
int retryCount = 3;
double price = 249.99;
boolean isLoggedIn = false;
// 4. Conditionals -- how assertions decide pass/fail underneath
if (retryCount > 0) {
System.out.println("Retrying " + retryCount + " times");
} else {
System.out.println("No retries left");
}
// 5. Loops -- iterate over test data
String[] browsers = {"chrome", "firefox", "edge"};
for (String browser : browsers) {
System.out.println("Would run on: " + browser);
}
// 6. Method calls on objects -- the core of Selenium
System.out.println(username.toUpperCase());
System.out.println(username.contains("@"));
System.out.printf("Price: %.2f%n", price);
}
}
Retrying 3 times
Would run on: chrome
Would run on: firefox
Would run on: edge
QA.USER@EXAMPLE.COM
true
Price: 249.99
A class worth testing¶
Create src/main/java/com/example/Calculator.java — Module 07 will test it:
package com.example;
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public int subtract(int a, int b) {
return a - b;
}
public double divide(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("Cannot divide by zero");
}
return (double) a / b;
}
/** Returns the discount percentage for an order amount (see Module 05). */
public int discountFor(double orderAmount) {
if (orderAmount < 0) {
throw new IllegalArgumentException("Order amount cannot be negative");
}
if (orderAmount >= 10000) return 15;
if (orderAmount >= 5000) return 10;
if (orderAmount >= 1000) return 5;
return 0;
}
}
Notice discountFor implements exactly the tiered rule you partitioned in
Module 05. In Module 07 you will turn those partitions and boundaries into
executable JUnit tests — that is the bridge between manual technique and
automation.
7. The Maven commands you will use¶
| Command | What it does |
|---|---|
mvn clean |
Deletes target/ — old compiled classes and reports |
mvn compile |
Compiles src/main/java |
mvn test-compile |
Compiles src/test/java |
mvn test |
Compiles everything and runs the test suite |
mvn clean test |
The one you will type most — a guaranteed-fresh run |
mvn test -Dtest=CalculatorTest |
Run one test class |
mvn test -Dtest=CalculatorTest#addsTwoPositiveNumbers |
Run one test method |
mvn dependency:tree |
Show all dependencies and where they came from |
mvn -v |
Version check |
Reports land in target/surefire-reports/ as .txt and .xml — the XML is
what CI servers and reporting tools (Level 2, Module 08) consume.
8. Troubleshooting¶
| Symptom | Cause and fix |
|---|---|
mvn: command not found |
Maven's bin is not on PATH |
Source option 21 is no longer supported |
Your JDK is older than the maven.compiler.source in pom.xml — lower it to 17 or install JDK 21 |
No tests were executed! |
Test class is outside src/test/java, or its name does not match *Test/Test* |
Could not resolve dependencies |
Typo in a coordinate, or no network — check central.sonatype.com for the exact version |
| Build hangs on first run | Maven is downloading the internet into ~/.m2/repository. It is a one-time cost |
IDE shows red imports but mvn test works |
Reimport the Maven project in the IDE (IntelliJ: the Maven panel's refresh icon) |
Cheat sheet¶
| Item | Value |
|---|---|
| JDK version | 17 or 21 (LTS) |
| Verify Java | java -version |
| Build tool | Maven — mvn -version |
| Project definition | pom.xml |
| Test code location | src/test/java |
| Test data location | src/test/resources |
| Run all tests | mvn clean test |
| Run one class | mvn test -Dtest=ClassName |
| Dependency scope for test libs | <scope>test</scope> |
| Reports | target/surefire-reports/ |
How It Actually Works¶
javac and java are two separate programs for a reason worth
understanding, because it explains almost every "it compiled but won't
run" problem you'll hit: javac reads .java source files and emits
.class files containing JVM bytecode — a platform-independent
instruction set, not native machine code. java then launches the JVM,
which loads those .class files through a classloader (resolving every
import against the classpath you provide), verifies the bytecode for type
safety, and either interprets it directly or hands hot methods to the
JIT compiler to be compiled to native code on the fly. This is exactly
why JAVA_HOME matters independently of PATH: build tools like Maven
invoke the JDK's compiler and JVM programmatically by resolving
$JAVA_HOME/bin/javac, not by trusting whatever java happens to resolve
to on your shell's PATH — a machine with three JDKs installed can have a
PATH pointing at Java 8 while JAVA_HOME (and therefore every Maven
build) uses Java 21, silently, until a --release mismatch error surfaces
it. Maven itself is a thin Java program that reads pom.xml, resolves each
<dependency> against a local cache (~/.m2/repository) and remote
repositories, builds a dependency graph, and then invokes the JVM (via
javac for compilation, then plugins like Surefire for test execution) —
so "download JUnit" really means Maven fetching a .jar of compiled
bytecode into ~/.m2 and adding it to the classpath the JVM launches with.
Exercise¶
- Install a JDK (17 or 21) and Maven. Paste the output of
java -versionandmvn -versioninto a file calledsetup-verification.txtas proof. - Create the
java-testing-practiceproject exactly as laid out in section 4, with thepom.xmlfrom section 5. Runmvn dependency:resolveand confirm all three libraries download successfully. - Add the
Calculatorclass from section 6 undersrc/main/java/com/example/, then runmvn compileand confirm you seeBUILD SUCCESS. - Run
mvn dependency:treeand answer: how many transitive dependencies doesselenium-javapull in that you did not declare yourself? Name three of them. - Deliberately break something: change the Selenium version to
4.99.0and runmvn dependency:resolve. Read the error message carefully, then fix it. Being able to read a Maven failure is a genuine job skill.