Skip to content

toString and Private Fields

toString turns a memory code into a readable result log. private plus a getter turns an open field into a door you control — the exact shape of every Page Object.

2 min read

Try to print an object directly and Java, not knowing what matters to you, prints the type name and a memory code — something like TestResult@1b6d3586, which helps no one. You fix that by giving the class a toString method that returns the text you want:

class TestResult {
    String name;
    String status;
    int durationMs;

    TestResult(String name, String status, int durationMs) {
        this.name = name;
        this.status = status;
        this.durationMs = durationMs;
    }

    public String toString() {
        return name + " [" + status + ", " + durationMs + "ms]";
    }
}

Now the same list of objects from Chapter 5, printed, reads like a report:

List<TestResult> suite = new ArrayList<>();
suite.add(new TestResult("LoginTest", "PASS", 240));
suite.add(new TestResult("SearchTest", "FAIL", 512));
suite.add(new TestResult("CartTest", "PASS", 180));
for (TestResult r : suite) {
    System.out.println(r);
}
LoginTest [PASS, 240ms]
SearchTest [FAIL, 512ms]
CartTest [PASS, 180ms]

Whenever Java needs the text form of your object — in println, in a joined String, in a printed list — it calls your toString. This one method turns a wall of memory codes into a readable result log.

Controlling access: private fields

So far any code can reach in and change a field: r.status = "whatever". Sometimes that's fine, but often you want to protect an object's data so it can only be read, or only be set through a check. You do that by marking the field private and offering a method to read it — a getter:

class PageObject {
    private String url;

    PageObject(String url) {
        this.url = url;
    }

    String getUrl() {
        return url;
    }
}
PageObject login = new PageObject("https://uat.shopcart.internal/login");
System.out.println("Login page URL: " + login.getUrl());
Login page URL: https://uat.shopcart.internal/login

private means "only code inside this class may touch this field directly." Outside code must go through getUrl(). Right now the getter just hands the value back, but the point is control: later you could add a check, a log, or a default, in one place, and nothing outside would need to change.

This pattern — private data, public methods — is the reason the Page Object classes in every real Selenium framework look the way they do. You're already reading their shape: private fields for the page's elements, methods for the actions a user can take, a constructor that receives the driver. A test-data model — a User, an Order, a Product — is a class with fields and a constructor, often built straight from a row of a data file. You're not learning toward real automation code; you're learning the exact material it's made of.

Why make a field private if you just add a getter that returns it? Because the getter is a door you control and the field is not. Today the getter only returns the value. The day you need to log every read, validate a set, or compute the value instead of storing it, you change one method and no outside code breaks. Private fields keep that freedom.

This article is part of Automation Foundations, in the Automation Engineering track. See the full sequence to find what comes next.