CommandsIntermediate6-8 min reference
Core Java for Testers
A focused tour of the Java features you'll actually use in test automation: data types, collections, exception handling, streams, and the patterns that show up in every Selenium/REST-Assured/JUnit project.
Data Types & Variables
Primitive types
int n = 42; // 32-bit integer (-2^31 .. 2^31-1)
long id = 9_000_000_000L; // 64-bit integer, requires L suffix
double pi = 3.14159; // 64-bit floating point
float f = 1.5f; // 32-bit float, requires f suffix
boolean ok = true; // true | false
char c = 'A'; // single 16-bit Unicode character
byte b = 127; // 8-bit (-128 .. 127)
short s = 30000; // 16-bitString creation, methods, and pitfalls
String name = "QA Engineer";
name.length(); // 11
name.substring(3, 8); // "Engin"
name.contains("Engineer"); // true
name.startsWith("QA"); // true
name.endsWith("er"); // true
name.toLowerCase(); // "qa engineer"
name.toUpperCase();
name.trim(); // strip leading/trailing whitespace
name.strip(); // Unicode-aware (Java 11+)
name.replace("QA", "Test"); // "Test Engineer"
name.split(" "); // ["QA", "Engineer"]
String.join(", ", "a", "b", "c"); // "a, b, c"
String.format("user=%s id=%d", name, 42);== vs .equals()
String a = "qa";
String b = "qa";
String c = new String("qa");
a == b; // true — same string pool reference
a == c; // false — different objects
a.equals(c); // true — value comparison
a.equalsIgnoreCase("QA"); // trueAlways use .equals() for string comparison. == checks reference identity, not content.
Type casting
double d = 3.99;
int i = (int) d; // 3 (truncates, no rounding)
int n = Integer.parseInt("123");
double x = Double.parseDouble("3.14");
String s = String.valueOf(42); // "42"
// Boxing / unboxing
Integer boxed = 42; // auto-box int → Integer
int unboxed = boxed; // auto-unbox Integer → intvar (Java 10+)
var list = new ArrayList<String>(); // ArrayList<String>
var users = Map.of("alice", 1, "bob", 2); // Map<String, Integer>
var response = restTemplate.getForObject(url, User.class);
// var is local-only — can't use for fields, params, or null without explicit typeOOP Fundamentals
Class and object
public class User {
private String name;
private int age;
public User(String name, int age) { // constructor
this.name = name;
this.age = age;
}
public User() { // overloaded default constructor
this("Anonymous", 0);
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
User u = new User("Ada", 36);Access modifiers
| Modifier | Same class | Same package | Subclass | Anywhere |
|---|---|---|---|---|
public | ✓ | ✓ | ✓ | ✓ |
protected | ✓ | ✓ | ✓ | ✗ |
| (default) | ✓ | ✓ | ✗ | ✗ |
private | ✓ | ✗ | ✗ | ✗ |
Inheritance
public class BasePage {
protected WebDriver driver;
public BasePage(WebDriver driver) {
this.driver = driver;
}
public String getTitle() {
return driver.getTitle();
}
}
public class LoginPage extends BasePage {
public LoginPage(WebDriver driver) {
super(driver); // call parent constructor
}
@Override
public String getTitle() { // method override
return "Login — " + super.getTitle(); // call parent method
}
}Abstract classes
public abstract class TestBase {
protected WebDriver driver;
@BeforeEach
public abstract void setUp(); // subclasses must implement
@AfterEach
public void tearDown() { // shared concrete method
if (driver != null) driver.quit();
}
}Interfaces
public interface Searchable {
void search(String term);
default boolean isEmpty(String term) { // default method (Java 8+)
return term == null || term.isBlank();
}
}
public class GoogleSearch implements Searchable {
@Override
public void search(String term) { /* ... */ }
}Polymorphism: overloading vs overriding
// Overloading — same method name, different parameters
public class Logger {
public void log(String msg) { /* ... */ }
public void log(String msg, int level) { /* ... */ }
public void log(Throwable err) { /* ... */ }
}
// Overriding — subclass replaces parent's method
@Override
public String toString() {
return "User[" + name + "]";
}instanceof and pattern matching (Java 16+)
Object o = getValue();
if (o instanceof String s) { // pattern matching binds 's'
System.out.println(s.toUpperCase());
} else if (o instanceof Integer i && i > 0) {
System.out.println("positive int: " + i);
}static
public class TestConfig {
public static final String BASE_URL = "https://staging.example.com";
private static TestConfig instance;
public static TestConfig getInstance() { /* ... */ }
public static long timestamp() {
return System.currentTimeMillis();
}
}
TestConfig.timestamp(); // call without instance
TestConfig.BASE_URL; // access constant directlyCollections Framework
List
List<String> users = new ArrayList<>();
users.add("alice");
users.add("bob");
users.add(0, "admin"); // insert at index
users.get(0); // "admin"
users.set(1, "ada"); // replace at index
users.remove("bob");
users.size();
users.contains("ada"); // true
users.isEmpty(); // false
users.indexOf("ada"); // 1
// Immutable
List<String> roles = List.of("admin", "editor", "viewer");
// Convert array → list
List<String> fromArray = Arrays.asList("a", "b", "c");ArrayList for fast random access; LinkedList only when you need Queue/Deque semantics.
Set
Set<String> tags = new HashSet<>();
tags.add("smoke");
tags.add("regression");
tags.add("smoke"); // ignored — duplicate
tags.size(); // 2
tags.contains("smoke"); // true
tags.remove("regression");
// Sorted
Set<String> sorted = new TreeSet<>(tags);
// Immutable
Set<String> envs = Set.of("dev", "stg", "prod");Map
Map<String, Integer> scores = new HashMap<>();
scores.put("alice", 95);
scores.put("bob", 88);
scores.get("alice"); // 95
scores.getOrDefault("missing", 0); // 0
scores.containsKey("alice");
scores.containsValue(95);
scores.remove("bob");
scores.size();
// Iterate
for (Map.Entry<String, Integer> e : scores.entrySet()) {
System.out.println(e.getKey() + " = " + e.getValue());
}
scores.keySet(); // Set<String>
scores.values(); // Collection<Integer>
// Immutable
Map<String, String> env = Map.of("BASE_URL", "https://api.example.com",
"TIMEOUT", "30");Queue (via LinkedList)
Queue<String> q = new LinkedList<>();
q.offer("first"); // add to tail
q.offer("second");
q.peek(); // "first" — head, no remove
q.poll(); // "first" — head, removeIterating
List<String> users = List.of("ada", "bob", "cy");
for (int i = 0; i < users.size(); i++) {
System.out.println(users.get(i));
}
for (String u : users) { // for-each
System.out.println(u);
}
users.forEach(System.out::println); // method reference
Iterator<String> it = users.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}Sorting and unmodifiable
List<Integer> nums = new ArrayList<>(List.of(3, 1, 2));
Collections.sort(nums); // [1, 2, 3]
Collections.sort(nums, Comparator.reverseOrder());
nums.sort((a, b) -> a - b);
List<Integer> readonly = Collections.unmodifiableList(nums);
// readonly.add(4); → UnsupportedOperationExceptionException Handling
try / catch / finally
try {
driver.findElement(By.id("submit")).click();
} catch (NoSuchElementException e) {
log.warn("Submit not found", e);
} catch (ElementNotInteractableException | StaleElementReferenceException e) {
log.warn("Element issue", e);
} catch (Exception e) {
log.error("Unexpected", e);
throw new RuntimeException("Test failed", e);
} finally {
driver.quit(); // always runs
}throw and throws
public User loadUser(String id) throws UserNotFoundException {
if (id == null) throw new IllegalArgumentException("id required");
User u = repo.find(id);
if (u == null) throw new UserNotFoundException(id);
return u;
}Checked vs unchecked
- Checked (extends
Exception, notRuntimeException) — caller must declarethrowsor catch. Examples:IOException,SQLException. - Unchecked (extends
RuntimeException) — propagate freely. Examples:NullPointerException,IllegalStateException,ArrayIndexOutOfBoundsException,ClassCastException.
Common exceptions
NullPointerException // calling a method on null
ArrayIndexOutOfBoundsException // array index < 0 or >= length
ClassCastException // bad cast
NumberFormatException // Integer.parseInt("abc")
IllegalArgumentException // invalid arg passed by caller
IllegalStateException // method called at wrong lifecycle point
IOException // file/network errors (checked)Custom exception
public class TestSetupException extends RuntimeException {
public TestSetupException(String msg) { super(msg); }
public TestSetupException(String msg, Throwable cause) { super(msg, cause); }
}try-with-resources
Auto-closes any AutoCloseable (files, JDBC connections, HTTP clients):
try (BufferedReader r = Files.newBufferedReader(Path.of("test.csv"))) {
String line;
while ((line = r.readLine()) != null) {
process(line);
}
} // r.close() called automaticallyStreams & Lambdas (Java 8+)
Lambda syntax
Runnable r1 = () -> System.out.println("run");
Consumer<String> c1 = s -> System.out.println(s);
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
Function<String, Integer> len = s -> { // multi-line block
String trimmed = s.trim();
return trimmed.length();
};Functional interfaces
Predicate<String> notBlank = s -> s != null && !s.isBlank();
Function<String, Integer> length = String::length;
Consumer<String> print = System.out::println;
Supplier<LocalDateTime> now = LocalDateTime::now;
BiFunction<String, String, String> concat = (a, b) -> a + b;Stream pipeline
List<User> users = repo.all();
List<String> activeAdmins = users.stream()
.filter(u -> u.isActive())
.filter(u -> u.getRole() == Role.ADMIN)
.map(User::getEmail)
.sorted()
.distinct()
.limit(50)
.collect(Collectors.toList());Common terminal operations
long count = users.stream().filter(User::isActive).count();
Optional<User> first = users.stream()
.filter(u -> u.getId() == 42)
.findFirst();
boolean anyAdmin = users.stream().anyMatch(u -> u.getRole() == Role.ADMIN);
boolean allActive = users.stream().allMatch(User::isActive);
boolean noneBanned = users.stream().noneMatch(User::isBanned);
users.stream().forEach(System.out::println);
int totalAge = users.stream().mapToInt(User::getAge).sum();Collectors
import static java.util.stream.Collectors.*;
List<String> emails = stream.map(User::getEmail).collect(toList());
Set<String> unique = stream.map(User::getEmail).collect(toSet());
Map<Integer, User> byId = users.stream().collect(toMap(User::getId, u -> u));
Map<Role, List<User>> byRole = users.stream()
.collect(groupingBy(User::getRole));
Map<Role, Long> counts = users.stream()
.collect(groupingBy(User::getRole, counting()));
String csv = users.stream()
.map(User::getName)
.collect(joining(", ", "[", "]")); // "[Ada, Bob, Cy]"Method references
String::toLowerCase // x -> x.toLowerCase()
System.out::println // x -> System.out.println(x)
Integer::parseInt // s -> Integer.parseInt(s)
User::new // (name, age) -> new User(name, age)File I/O
Reading
import java.nio.file.*;
// Whole file as String
String content = Files.readString(Path.of("test.json"));
// All lines
List<String> lines = Files.readAllLines(Path.of("data.csv"));
// Stream over lines (lazy, closes the file when stream is closed)
try (Stream<String> stream = Files.lines(Path.of("big.log"))) {
stream.filter(l -> l.contains("ERROR")).forEach(System.out::println);
}
// BufferedReader
try (BufferedReader r = Files.newBufferedReader(Path.of("test.txt"))) {
String line;
while ((line = r.readLine()) != null) {
process(line);
}
}Writing
Files.writeString(Path.of("out.txt"), "hello");
Files.write(Path.of("out.txt"), List.of("line 1", "line 2"));
try (BufferedWriter w = Files.newBufferedWriter(Path.of("out.txt"))) {
w.write("hello");
w.newLine();
}Properties files
config.properties:
base.url=https://staging.example.com
api.timeout=30000
admin.email=qa@example.com
Properties props = new Properties();
try (InputStream in = Files.newInputStream(Path.of("config.properties"))) {
props.load(in);
}
String baseUrl = props.getProperty("base.url");
int timeout = Integer.parseInt(props.getProperty("api.timeout", "10000"));JSON with Jackson
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper mapper = new ObjectMapper();
User u = mapper.readValue(new File("user.json"), User.class);
List<User> users = mapper.readValue(json,
mapper.getTypeFactory().constructCollectionType(List.class, User.class));
String out = mapper.writeValueAsString(u);
mapper.writerWithDefaultPrettyPrinter().writeValue(new File("out.json"), u);JSON with Gson
import com.google.gson.Gson;
Gson gson = new Gson();
User u = gson.fromJson(json, User.class);
String out = gson.toJson(u);Common Patterns in Test Automation
Builder pattern for test data
public class UserBuilder {
private String name = "Default";
private int age = 30;
private String email = "default@example.com";
public UserBuilder name(String n) { this.name = n; return this; }
public UserBuilder age(int a) { this.age = a; return this; }
public UserBuilder email(String e) { this.email = e; return this; }
public User build() { return new User(name, age, email); }
}
User u = new UserBuilder().name("Ada").age(36).build();Page Object Model
public class LoginPage {
private final WebDriver driver;
private final By emailField = By.cssSelector("[data-testid=email]");
private final By passwordField = By.cssSelector("[data-testid=password]");
private final By submitBtn = By.cssSelector("[data-testid=submit]");
public LoginPage(WebDriver driver) { this.driver = driver; }
public DashboardPage loginAs(String email, String password) {
driver.findElement(emailField).sendKeys(email);
driver.findElement(passwordField).sendKeys(password);
driver.findElement(submitBtn).click();
return new DashboardPage(driver);
}
}Factory pattern for driver creation
public class DriverFactory {
public static WebDriver create(String type) {
return switch (type.toLowerCase()) {
case "chrome" -> new ChromeDriver();
case "firefox" -> new FirefoxDriver();
case "safari" -> new SafariDriver();
default -> throw new IllegalArgumentException("Unknown: " + type);
};
}
}Singleton for config
public class TestConfig {
private static volatile TestConfig instance;
private final Properties props = loadProps();
private TestConfig() {}
public static TestConfig get() {
if (instance == null) {
synchronized (TestConfig.class) {
if (instance == null) instance = new TestConfig();
}
}
return instance;
}
public String baseUrl() { return props.getProperty("base.url"); }
}
String url = TestConfig.get().baseUrl();JUnit 5 annotations & assertions
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
class CheckoutTest {
@BeforeAll static void setupAll() { /* once before all tests */ }
@AfterAll static void teardownAll() {}
@BeforeEach void setup() { /* before every test */ }
@AfterEach void teardown() {}
@Test
@DisplayName("Cart total equals sum of items")
void cartTotalSum() {
Cart c = new Cart();
c.add(new Item(10), new Item(15));
assertEquals(25, c.total());
assertTrue(c.itemCount() > 0);
assertNotNull(c.id());
assertThrows(IllegalStateException.class, () -> c.checkout());
assertAll("cart",
() -> assertEquals(2, c.itemCount()),
() -> assertFalse(c.isEmpty()));
}
@Disabled("flaky on CI, see #1234")
@Test void brokenTest() {}
@ParameterizedTest
@ValueSource(strings = {"@example.com", "user@", ""})
void rejectsInvalidEmails(String email) {
assertThrows(ValidationException.class, () -> new User(email));
}
}