Break, Continue, and Nested Loops

7 min read

Plain loops walk every element. Real test code rarely wants that. You want to stop when you hit a critical failure, skip rows you've decided not to run, and combine loops to test every browser against every resolution. The keywords for those jobs are break and continue, and the technique for combining loops is nesting — a loop inside a loop. Java also adds one piece JavaScript doesn't have: labelled break/continue, which lets you jump out of an outer loop from inside an inner one.

break — exit the loop now

break stops the innermost loop immediately. Nothing else in the loop runs.

public class StopOnCritical {
    public static void main(String[] args) {
        String[] testCases = {"Login", "Search", "CRITICAL_FAILURE", "Checkout"};
 
        for (String testCase : testCases) {
            if (testCase.equals("CRITICAL_FAILURE")) {
                System.out.println("Critical failure detected — stopping the run");
                break;
            }
            System.out.println("Running: " + testCase);
        }
 
        System.out.println("Done.");
    }
}

Output:

Running: Login
Running: Search
Critical failure detected — stopping the run
Done.

Notice that Checkout never runs and the line after the loop (Done.) does. break exits the loop, not the method. Test runners use this pattern for "fail fast" mode — the moment one critical test fails, abort the rest.

continue — skip the rest of this iteration

continue jumps to the next iteration without running the rest of the body.

public class SkipDisabled {
    public static void main(String[] args) {
        String[] testCases = {"Login", "SKIP_FlakyAuth", "Search", "SKIP_LegacyExport", "Checkout"};
 
        for (String testCase : testCases) {
            if (testCase.startsWith("SKIP_")) {
                System.out.println("Skipping: " + testCase);
                continue;
            }
            System.out.println("Running: " + testCase);
        }
    }
}

Output:

Running: Login
Skipping: SKIP_FlakyAuth
Running: Search
Skipping: SKIP_LegacyExport
Running: Checkout

continue is break's milder sibling: stay in the loop, just don't run the rest of this round. In test code this shape filters out cases by tag or naming convention without nested if/else.

break vs continue — visualised

The two arrows out of continue and break are the only difference: continue loops back; break falls through to whatever comes after the loop.

Nested loops — testing combinations

A loop inside a loop is exactly what it sounds like. The most common QA use is generating a test matrix — every browser × every resolution × every locale.

public class BrowserResolutionMatrix {
    public static void main(String[] args) {
        String[] browsers = {"Chrome", "Firefox"};
        String[] resolutions = {"1920x1080", "1366x768", "375x667"};
 
        for (String browser : browsers) {
            for (String resolution : resolutions) {
                System.out.println("Testing: " + browser + " @ " + resolution);
            }
        }
    }
}

Output:

Testing: Chrome @ 1920x1080
Testing: Chrome @ 1366x768
Testing: Chrome @ 375x667
Testing: Firefox @ 1920x1080
Testing: Firefox @ 1366x768
Testing: Firefox @ 375x667

Two browsers × three resolutions = six combinations. The outer loop fixes the browser, the inner loop walks every resolution for that browser, then the outer loop advances. Nesting deeper (browser × resolution × locale) gets you more dimensions; the rule is the inner loop runs to completion for every iteration of the outer.

break and continue inside nested loops — the gotcha

By default, break and continue only affect the innermost loop they live in. Watch what happens when we want to skip a known-broken combination:

for (String browser : browsers) {
    for (String resolution : resolutions) {
        if (browser.equals("Firefox") && resolution.equals("375x667")) {
            System.out.println("Skipping: " + browser + " @ " + resolution);
            continue;        // ← skips THIS iteration of the inner loop
        }
        System.out.println("Testing: " + browser + " @ " + resolution);
    }
}

That's fine if you only want to skip the one combination. But what if hitting Firefox-mobile means "stop testing Firefox entirely and move to the next browser"? The plain continue keeps walking the inner loop — which is the resolution loop, not the browser loop. You need a way to tell Java: "this continue applies to the outer loop."

Labelled break and continue

Java lets you put a label on a loop and then break label or continue label to target it specifically. JavaScript also has labels but barely uses them; in Java they're occasionally indispensable.

public class FirefoxKnownIssue {
    public static void main(String[] args) {
        String[] browsers = {"Chrome", "Firefox", "Safari"};
        String[] resolutions = {"1920x1080", "1366x768", "375x667"};
 
        outer:
        for (String browser : browsers) {
            for (String resolution : resolutions) {
                if (browser.equals("Firefox") && resolution.equals("375x667")) {
                    System.out.println("Firefox mobile is broken — skipping rest of Firefox");
                    continue outer;        // jump to next BROWSER, not next resolution
                }
                System.out.println("Testing: " + browser + " @ " + resolution);
            }
        }
    }
}

Output:

Testing: Chrome @ 1920x1080
Testing: Chrome @ 1366x768
Testing: Chrome @ 375x667
Testing: Firefox @ 1920x1080
Testing: Firefox @ 1366x768
Firefox mobile is broken — skipping rest of Firefox
Testing: Safari @ 1920x1080
Testing: Safari @ 1366x768
Testing: Safari @ 375x667

continue outer jumps to the next iteration of the loop named outer — i.e., the next browser. break outer would exit both loops entirely. The label is just a name followed by a colon; it has to immediately precede the loop.

A note on labels: most Java teams treat them as a code smell because they hint at code that wants to be a method. If you find yourself reaching for break outer, ask whether extracting the inner loop into a helper method that returns true/false would be cleaner. That said, for test matrices labelled break is often the most direct option.

A test-matrix runner with skip rules

Pulling everything together — the kind of utility a real cross-browser test harness might contain:

public class TestMatrix {
    public static void main(String[] args) {
        String[] browsers = {"Chrome", "Firefox", "Safari"};
        String[] resolutions = {"1920x1080", "1366x768", "375x667"};
 
        int run = 0;
        int skipped = 0;
 
        outer:
        for (String browser : browsers) {
            for (String resolution : resolutions) {
                if (browser.equals("Safari") && resolution.equals("1366x768")) {
                    System.out.println("Skip Safari @ 1366x768 (no licence)");
                    skipped++;
                    continue;            // skip this one combo only
                }
                if (browser.equals("Firefox") && resolution.equals("375x667")) {
                    System.out.println("Firefox mobile broken — moving on");
                    skipped++;
                    continue outer;      // bail out of all remaining Firefox combos
                }
                System.out.println("Run #" + (++run) + ": " + browser + " @ " + resolution);
            }
        }
 
        System.out.println("Ran " + run + ", skipped " + skipped);
    }
}

Output:

Run #1: Chrome @ 1920x1080
Run #2: Chrome @ 1366x768
Run #3: Chrome @ 375x667
Run #4: Firefox @ 1920x1080
Run #5: Firefox @ 1366x768
Firefox mobile broken — moving on
Skip Safari @ 1366x768 (no licence)
Run #6: Safari @ 1920x1080
Run #7: Safari @ 375x667
Ran 7, skipped 2

continue outer skipped Firefox-mobile and advanced past it; the unlabelled continue only skipped the single Safari-1366 combo. Both are useful; pick the one that matches the rule you're encoding.

⚠️ Common mistakes

  • Expecting break in a nested loop to exit both loops. It only exits the innermost one. Use a label, restructure to a return from a helper method, or set a flag the outer loop checks.
  • Using continue when you meant break. continue keeps looping; break stops. If you find continue ending up in a loop that "won't stop on critical failure," you wanted break.
  • Putting heavy logic in the inner loop. Cross-products multiply. Three loops of 10 elements is 1,000 iterations; if the body makes an HTTP call, that's 1,000 HTTP calls. Test matrices grow faster than you expect — keep the inner-loop body cheap, or filter combinations before you start.

🎯 Practice task

Build a real cross-browser test matrix. 25-30 minutes.

  1. Create MatrixWithSkips.java.
  2. Declare three arrays:
    • String[] browsers = {"Chrome", "Firefox", "Safari", "Edge"};
    • String[] resolutions = {"1920x1080", "1440x900", "1366x768", "375x667"};
    • String[] locales = {"en-US", "en-GB"};
  3. Use a triple-nested loop to print every (browser, resolution, locale) combination, e.g. Run: Chrome @ 1920x1080 [en-US]. Count how many lines that is — it should be 4 × 4 × 2 = 32.
  4. Add a continue to skip every 375x667 combination on Edge (assume Edge mobile isn't supported).
  5. Add a labelled continue outer so that hitting Safari + en-GB ends all Safari testing immediately and jumps to the next browser.
  6. Track three counters: ran, skipped, aborted. Print the summary at the end: Ran X, skipped Y, aborted Z.
  7. Compile and run. Verify the printed combinations match what your skip rules say.
  8. Stretch: add a break that stops the entire matrix when ran reaches 10 — a "smoke run" mode. Confirm only ten combinations print before the loop exits.

You now have the full Chapter 2 toolkit: branching, comparison, logic, loops, and the keywords that shape how loops flow. Chapter 3 introduces methods and arrays — the building blocks for organising the code you've been writing entirely inside main.

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