Test Data Isolation and Independent Tests

Independent Tests

Every test must be able to run alone, in any order, in parallel. If checkoutTest only passes after addToCartTest, a failure in one hides the other, and parallel runs break.

  • Create the data each test needs in its own setup (preferably through the API, see the next module).
  • Use unique values: a fresh user, a unique order note, a unique coupon.
  • Do not use dependsOnMethods to share state between tests; use it only to skip tests that cannot work (e.g. after login is broken).
  • Clean up in @AfterMethod(alwaysRun = true) or use data that does not need cleanup.

One User per Thread

Example

Two parallel tests using the same user empty each other's cart. A pool hands out a different user to each test and takes it back afterwards:

import java.util.*;
import java.util.concurrent.*;

public class Main {
    static final BlockingQueue<String> USERS = new LinkedBlockingQueue<>(
            List.of("[email protected]", "[email protected]", "[email protected]"));

    public static void main(String[] args) throws Exception {
        ExecutorService pool = Executors.newFixedThreadPool(3);
        List<Future<String>> results = new ArrayList<>();
        for (String test : List.of("cartTest", "checkoutTest", "wishlistTest")) {
            results.add(pool.submit(() -> {
                String user = USERS.take();          // borrow a free user
                try {
                    Thread.sleep(30);                // the test runs
                    return test + " ran as a user with its own cart";
                } finally {
                    USERS.put(user);                 // give it back
                }
            }));
        }
        for (Future<String> f : results) System.out.println(f.get());
        pool.shutdown();
        System.out.println("Users back in the pool: " + USERS.size());
    }
}

Output

cartTest ran as a user with its own cart
checkoutTest ran as a user with its own cart
wishlistTest ran as a user with its own cart
Users back in the pool: 3

Practice

Exercise: CheckoutTest.placeOrder uses dependsOnMethods = "addToCart" to reuse the cart. Rewrite it so it is independent.

Show answer
@BeforeMethod
public void prepareCart() {
    // fresh user and cart for this test only (API is fastest, UI also works)
    user = TestUsers.borrow();
    api.addToCart(user, "SKU-HP-450", 1);
}

@Test
public void placeOrder() {
    String orderId = new LoginPage(driver).open().loginAs(user.email(), user.password())
            .openCart().proceedToCheckout().payWithCod().orderId();
    Assert.assertTrue(orderId.startsWith("ORD-"));
}

@AfterMethod(alwaysRun = true)
public void release() { TestUsers.giveBack(user); }