Types, String and the difference between == and equals
Java has two kinds of types: primitives (int, long, double, boolean) and reference types (classes, arrays, String, the wrappers Integer, Boolean). A primitive holds a value, a reference holds the address of an object, so it can be null.
== versus equals. For primitives == compares values. For objects == compares references (is it the same object) while equals compares content, if the class overrides it.
String a = "qa";
String b = new String("qa");
a == b; // false: different objects
a.equals(b); // true: same text
String literals go into a pool, so "qa" == "qa" is always true, but strings built at run time (from an API response, by concatenating variables, with new String) are separate objects, so you cannot rely on ==. In tests, strings are always compared with equals (or an assertion library), and with Objects.equals(a, b) when null is possible.
Pitfalls for an automation engineer:
- Wrappers:
Integer values from −128 to 127 are cached, so == "works" for small numbers and breaks for large ones. Compare with equals or unbox to int.
- Unboxing
null into a primitive throws NullPointerException: int id = response.getId(); when the field is an Integer holding null.
- Money and exact decimals:
BigDecimal built from a string (new BigDecimal("0.10")). double gives 0.1 + 0.2 != 0.3. Compare with compareTo: equals treats 2.0 and 2.00 as different.
Modern Java for tests: var, enum, record and text blocks
The current Java LTS is 25 (earlier 21, 17, 11). New automation projects should start on an LTS and use the features that remove boilerplate.
- enum — a fixed set of values: browsers, environments, roles. A typo in a name shows up at compile time, and a
switch over an enum lists the options explicitly.
enum Browser { CHROME, FIREFOX, EDGE }
Browser b = Browser.valueOf(System.getProperty("browser", "chrome").toUpperCase());
valueOf throws IllegalArgumentException for an unknown name, a fast and clear failure at start-up instead of odd behaviour in the middle of a run.
- record (Java 16) — an immutable data carrier: the compiler adds a constructor, accessors such as
email(), equals, hashCode and toString.
record Credentials(String email, String password) {}
Handy for test data and parameters. A record cannot be extended and cannot change after creation; Jackson (2.12+) can read and write records.
- var (Java 10) — local variable type inference:
var users = new ArrayList<User>();. The type stays static; var is not for fields and parameters. Do not overuse it when the type is not obvious from the right-hand side.
- Text blocks (Java 15) — multi-line strings in triple quotes, convenient for JSON bodies and SQL:
String body = """
{"email": "a@example.com", "role": "admin"}
""";
- Pattern matching for instanceof (
o instanceof User u) and switch expressions shorten type checks.
All of this is only syntax: it does not change test speed, but it makes test-data code shorter and safer.