Defining and Calling Methods

8 min read

A method is Java's word for a reusable block of code — exactly what JavaScript and Python call a function. Up to now every line you've written has lived inside public static void main, which gets ugly fast: a single method that does everything is a method that does nothing well. Methods let you split a test framework into named, reusable pieces — loginAsAdmin(), validateResponse(...), formatDuration(...) — that you can call from anywhere. This lesson covers the shape of a method definition, how to call one, and the differences from JavaScript functions that catch beginners off guard.

The shape of a method

Every method has the same anatomy:

accessModifier  returnType  methodName(parameters)  { body }

A real example:

public static String getGreeting(String name) {
    return "Hello, " + name + "!";
}

Read left to right:

  • public — the access modifier. public means anyone can call it. Other options are private (only this class), protected (this class and subclasses), and the default (same package). For now, use public.
  • static — belongs to the class itself rather than to an instance of the class. We'll come back to this in a moment.
  • String — the return type. The method must give back a String value. Use void if it returns nothing.
  • getGreeting — the method name. Convention is camelCase starting with a verb: getGreeting, runTest, validateResponse. Names should describe an action.
  • (String name) — the parameter list. Each parameter is Type name. Multiple parameters are comma-separated.
  • { ... } — the method body. The code that runs when the method is called.
  • return ...; — the value the caller receives. The expression's type must match the return type.

Calling a method

Calling a method runs its body and gives you back the return value:

public class Greeter {
    public static String getGreeting(String name) {
        return "Hello, " + name + "!";
    }
 
    public static void main(String[] args) {
        String message = getGreeting("Alice");
        System.out.println(message);
        System.out.println(getGreeting("Bob"));
    }
}

Output:

Hello, Alice!
Hello, Bob!

The first call stores the return value in message. The second passes it straight to println. Both are valid uses of the same method.

void — methods that return nothing

When a method's job is to do something rather than compute a value, declare its return type as void:

public static void logResult(String message) {
    System.out.println("[LOG] " + message);
}

A void method has no return statement at the end (you can write a bare return; to exit early, but you don't have to). You call a void method as a statement on its own:

logResult("Login test passed");        // no value to capture
String x = logResult("...");           // ❌ compile error — can't store void

println, assertEquals, and most "do this thing" methods are void.

static — for now, treat it as required

You'll see static on every method in this chapter. Briefly:

  • A static method belongs to the class. You call it with ClassName.method(...) (or just method(...) from inside the same class).
  • An instance method belongs to an object — an instance of a class. You call it with myObject.method(...).

Until chapter 4, when you'll start creating objects, every method you write should be static. That lets main (which is itself static) call them directly without needing an instance. Once we cover constructors and new, instance methods become the norm.

Methods with no parameters

If the method needs no input, the parameter list is just empty parentheses:

public static int getDefaultTimeout() {
    return 5000;
}
 
public static void main(String[] args) {
    int timeout = getDefaultTimeout();
    System.out.println("Default timeout: " + timeout + "ms");
}

Output:

Default timeout: 5000ms

Even when there are zero parameters, the parentheses are not optional — getDefaultTimeout (no parens) does not call the method, it's a method reference. We'll come back to those in chapter 8.

A real QA helper — validateResponse

The whole reason methods exist is to take a code shape that would otherwise be repeated across every test and pull it into one named place. A response validator is a perfect example:

public class ResponseHelpers {
 
    public static boolean validateResponse(int statusCode, String body, long durationMs, long slaMs) {
        if (statusCode < 200 || statusCode >= 300) return false;
        if (body == null || body.isEmpty()) return false;
        if (durationMs >= slaMs) return false;
        return true;
    }
 
    public static void logResult(String testName, boolean passed) {
        String label = passed ? "✅ PASS" : "❌ FAIL";
        System.out.println(label + " " + testName);
    }
 
    public static void main(String[] args) {
        boolean loginOk = validateResponse(200, "{\"id\":42}", 1450, 2000);
        boolean searchOk = validateResponse(500, "", 800, 2000);
 
        logResult("Login", loginOk);
        logResult("Search", searchOk);
    }
}

Output:

✅ PASS Login
❌ FAIL Search

Read what the main method actually does: it makes two validateResponse calls, then logs each result. All the rules — status range, empty body, SLA — live in one named place. The day a fourth rule is added (e.g., must contain a specific header), you change validateResponse and every test that calls it picks up the new rule for free. That is the entire reason for methods.

The anatomy of a method, visualised

Five parts, always in the same order. Once you can read the parts left to right, every method you'll see in Selenium, TestNG, or Rest Assured documentation parses immediately.

Method naming — be a verb

Names matter because they're how future-you reads your test framework. Conventions every Java team follows:

  • camelCase, starting with a lowercase letter: validateResponse, not ValidateResponse (PascalCase is for class names) and not validate_response (snake_case is for Python).
  • Start with a verb. Methods do things. Good: calculatePassRate, findByTestId, loginAsAdmin. Less good: passRate, testId, admin — they read like nouns and tell the reader what the result is, not what the method does.
  • get / set for property-style accessors (you'll meet these in chapter 4): getStatusCode(), setBaseUrl(...). is / has for booleans: isLoggedIn(), hasErrors().
  • Be specific. runTest is fine; loginTest is better; loginWithInvalidPasswordShouldFail is what a test method itself is named.

⚠️ Common mistakes

  • Forgetting static — and getting "non-static method cannot be referenced from a static context." This compile error happens when main (static) tries to call a method that isn't static. Until chapter 4, mark every helper method static and the error goes away. Don't fix it by making fields static — that's the wrong direction.
  • Calling a method without parentheses. getGreeting is a reference to the method; getGreeting("Alice") actually calls it. Beginners often write String x = getGreeting; and get a confusing compile error. Always include the parens — even when the method takes zero arguments: getDefaultTimeout().
  • Declaring a non-void return type and forgetting to return. public static int retries() { System.out.println("hi"); } won't compile — Java demands a return along every code path. The fix is either to add return 0; or change the type to void.

🎯 Practice task

Extract reusable test helpers. 25-30 minutes.

  1. Create TestHelpers.java.

  2. Define a method public static String formatDuration(long ms) that returns a String like 1.45s for 1450 ms, or 850ms for values under 1000. Hint: if (ms < 1000) return ms + "ms"; return (ms / 1000.0) + "s";.

  3. Define a method public static double passRate(int passed, int total) that returns the pass rate as a percentage (e.g. 87.5 for 7 of 8). Use (double) passed / total * 100.

  4. Define a void method printSummary(String suite, int passed, int failed, long durationMs) that prints three lines using the previous helpers. Sample output for "smoke", 8, 2, 12450:

    Suite: smoke
    8 passed, 2 failed (80.0%)
    Total time: 12.45s
    
  5. In main, call printSummary twice with different inputs. Compile and run.

  6. Stretch: add a method public static String label(boolean passed) that returns "✅ PASS" or "❌ FAIL". Replace the inline ternaries in your other methods with calls to label(...). Notice how a tiny extraction tightens every place that needs the same label.

You can now factor your code out of main. Lesson 2 expands on parameters and shows how Java handles multiple methods with the same name — a feature you'll use constantly in real test frameworks.

// tip to track lessons you complete and pick up where you left off across devices.