Why this matters on the job
Walk into an automation interview at a service company (TCS, Infosys, Wipro, Cognizant, Capgemini, Accenture) or a product company in Bengaluru, Pune or Hyderabad and the first technical round is usually Java, not Selenium: reverse a string, find duplicates, explain final vs finally, explain why we write WebDriver driver = new ChromeDriver(). Candidates who know Selenium commands but freeze on Java rarely clear that round.
On the job it is the same story. Your daily work is reading and extending a framework written in Java: page classes, base classes, utilities, listeners and data readers. This lesson gives you exactly the Java a tester needs - no more - and builds the Maven project that every later lesson in this course grows into a full framework.
Concepts
The Java you will actually use in a Selenium framework
| Java concept | Where you meet it in the framework |
|---|---|
| Class and object | Every page (LoginPage) and every test class |
Inheritance (extends) | LoginTest extends BaseTest, LoginPage extends BasePage |
| Interface | WebDriver, WebElement, ITestListener are interfaces; ChromeDriver implements WebDriver |
| Encapsulation | Locators are private; tests call public business methods like loginAs() |
| Collections (List, Set, Map) | findElements() returns List<WebElement>; test data rows as Map<String, String> |
| Exceptions | NoSuchElementException, TimeoutException; try-with-resources for files |
static and final | Constants, utility classes, the ThreadLocal driver holder |
| Lambdas and streams | Turning a list of price elements into numbers and checking the sort order |
Here is a preview of how these ideas show up together in real test code (you will run this kind of code from lesson 2):
WebDriver driver = new ChromeDriver(); // interface reference, class object (upcasting)
List<WebElement> prices = driver.findElements(By.className("inventory_item_price"));
List<Double> values = prices.stream()
.map(WebElement::getText) // "$29.99"
.map(t -> Double.parseDouble(t.replace("$", ""))) // 29.99
.toList(); // Java 16+The toolchain
| Tool | Version used in this course | Why |
|---|---|---|
| JDK | 17 LTS (Eclipse Temurin); 21 also works | Long-term support, records, switch expressions, var |
| Maven | 3.9.x | Downloads libraries, compiles, runs TestNG through the Surefire plugin |
| IntelliJ IDEA | Community edition (free) | Best Maven/TestNG integration, refactoring, debugger |
| Git | 2.x | Every framework lives in a repository (lesson 9 and capstone) |
Maven essentials: the pom.xml declares dependencies by coordinates (groupId, artifactId, version). Maven downloads them into ~/.m2/repository. Source code lives in the standard layout src/main/java and src/test/java; resources (config, test data) go in src/test/resources. The test phase compiles everything and runs tests through the Surefire plugin. In this course all framework code (pages, utilities, tests) lives under src/test/java, the common convention for test-only projects.
Hands-on Lab: Install the toolchain and run your first Maven + TestNG build
- Install JDK 17 and verify. macOS:
brew install --cask temurin@17. Windows:winget install EclipseAdoptium.Temurin.17.JDK. Ubuntu:sudo apt install openjdk-17-jdk. Then run:
Setjava -version # expected: openjdk version "17.0.x" ...JAVA_HOMEto the JDK folder ifmvn -vlater reports a different Java. - Install Maven. macOS:
brew install maven. Ubuntu:sudo apt install maven. Windows: download the binary zip from maven.apache.org, extract toC:\tools\mavenand add itsbinfolder to PATH. Verify:mvn -v # expected: Apache Maven 3.9.x ... Java version: 17.0.x - Generate the project skeleton:
Delete the generatedmvn archetype:generate -DgroupId=com.qaji -DartifactId=selenium-framework \ -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.5 \ -DinteractiveMode=false cd selenium-frameworkApp.javaandAppTest.java- we will use TestNG, not JUnit. - Replace
pom.xmlwith the version below. It pins Java 17 and adds Selenium and TestNG. <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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.qaji</groupId> <artifactId>selenium-framework</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.release>17</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <!-- Check Maven Central for the newest 4.x / 7.x before you start --> <selenium.version>4.35.0</selenium.version> <testng.version>7.11.0</testng.version> </properties> <dependencies> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>${selenium.version}</version> </dependency> <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>${testng.version}</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.14.0</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.5.3</version> </plugin> </plugins> </build> </project>- Open the folder in IntelliJ (File > Open > select pom.xml > Open as Project) and click Load Maven Changes when prompted.
- Create
src/test/java/com/qaji/utils/PriceUtils.java. It practises static methods, exceptions, collections and streams - the exact skills used later to validate product prices on SauceDemo.package com.qaji.utils; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public final class PriceUtils { private PriceUtils() { } // utility class: no objects needed /** "$29.99" -> 29.99 */ public static double parsePrice(String text) { if (text == null || text.isBlank()) { throw new IllegalArgumentException("Price text is empty"); } return Double.parseDouble(text.replace("$", "").replace(",", "").trim()); } /** true when the list is already in ascending order */ public static boolean isSortedLowToHigh(List<Double> prices) { List<Double> copy = new ArrayList<>(prices); Collections.sort(copy); return copy.equals(prices); } /** returns only the items that appear more than once, with their count */ public static Map<String, Long> countDuplicates(List<String> items) { return items.stream() .collect(Collectors.groupingBy(s -> s, Collectors.counting())) .entrySet().stream() .filter(e -> e.getValue() > 1) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } } - Create
src/test/java/com/qaji/utils/PriceUtilsTest.java:package com.qaji.utils; import java.util.List; import java.util.Map; import org.testng.Assert; import org.testng.annotations.Test; public class PriceUtilsTest { @Test public void parsesDollarPrice() { Assert.assertEquals(PriceUtils.parsePrice("$29.99"), 29.99, 0.001); } @Test public void detectsSortedList() { Assert.assertTrue(PriceUtils.isSortedLowToHigh(List.of(7.99, 9.99, 15.99))); Assert.assertFalse(PriceUtils.isSortedLowToHigh(List.of(9.99, 7.99))); } @Test public void findsDuplicateProducts() { Map<String, Long> dups = PriceUtils.countDuplicates( List.of("Backpack", "Bike Light", "Backpack")); Assert.assertEquals(dups, Map.of("Backpack", 2L)); } @Test(expectedExceptions = IllegalArgumentException.class) public void rejectsEmptyPrice() { PriceUtils.parsePrice(" "); } } - Run the build from the project root:
mvn clean test # expected (end of output): # Tests run: 4, Failures: 0, Errors: 0, Skipped: 0 # BUILD SUCCESS - Break it on purpose: change
29.99to29.98in the first test, run again and read the failure message (expected [29.98] but found [29.99]). Revert, then commit:git init && git add . && git commit -m "Project skeleton".
Common mistakes
JAVA_HOMEstill points to Java 8 - Maven then fails with release version 17 not supported. FixJAVA_HOMEand reopen the terminal.- Mixing JUnit and TestNG imports:
org.junit.Assertin a TestNG test compiles only if JUnit is on the classpath and confuses reports. Always importorg.testng.Assert. - Naming a test class
PriceCheck: Surefire only picks classes matching*Test,Test*,*Testsor*TestCaseby default, so the build says Tests run: 0. - Comparing strings with
==instead ofequals()- a classic interview trap and a real source of false failures. - Editing
pom.xmlbut forgetting to reload Maven in IntelliJ, then wondering why imports are red.
Real-world assignment
Your test lead says: "Set up the shared automation repo before the sprint starts." Deliver the following in your own GitHub repository:
- Packages
com.qaji.base,pages,tests,utilscreated (empty placeholder classes are fine). - A
StringUtilsclass with three interview favourites and a TestNG test for each: reverse a string, count occurrences of each character (return aMap<Character, Integer>), and return the second-highest value from aList<Double>. - A
README.mdwith prerequisites (JDK 17, Maven 3.9) and the commandmvn clean test. - A
.gitignorecontainingtarget/,.idea/and*.iml.
Key takeaways
- Interview round one is Java: classes, inheritance, interfaces, collections, exceptions and strings.
WebDriver driver = new ChromeDriver()is upcasting to an interface - it lets the same code run on any browser.- Maven manages dependencies and runs tests;
pom.xmlplus the standard folder layout is the backbone of every framework. - Keep test class names ending in
Testso Surefire discovers them. - Commit a clean, building skeleton before writing any Selenium code.