Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Set up Selenium Java and TestNG in your build, create one isolated WebDriver per test method, synchronize with explicit waits, and let Maven Surefire discover or run your TestNG suite. The pattern below works for a maintainable Java test project; replace the example URL, selectors, credentials, browser, and driver setup with those from your application.
What you need before writing a test
- A supported JDK. TestNG’s official examples use TestNG 7.5.1 for JDK 8 and 7.9.0 for JDK 11. Treat those as examples and verify the version that matches your project.
- Maven or Gradle.
- A Selenium-compatible browser and driver available to the test process. Keep browser, driver, and Selenium Java versions compatible.
- A test environment whose URL, test account, and selectors are stable enough for automation.
Pin the dependency versions in source control. There is no single universal Selenium/TestNG version matrix, so check the current Selenium Java release and your JDK support policy before choosing versions.
Add Selenium and TestNG to the build
Maven
Put this in pom.xml. The Selenium version is deliberately a project property: set it to the current Selenium Java release you have selected, rather than copying an unqualified version number.
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<selenium.version>your-selected-selenium-version</selenium.version>
<testng.version>7.9.0</testng.version>
</properties>
<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
If your project remains on JDK 8, use a TestNG release compatible with that JDK, such as the 7.5.1 example documented by TestNG. Do not assume that the JDK 11 example is valid for JDK 8.
Gradle
The equivalent Gradle declaration is:
repositories {
mavenCentral()
}
dependencies {
testImplementation("org.seleniumhq.selenium:selenium-java:<selected Selenium version>")
testImplementation("org.testng:testng:7.9.0")
}
test {
useTestNG()
}
Replace the marked Selenium value with the current release chosen for your project and commit that value. Gradle’s useTestNG() tells the test task to use TestNG instead of its default engine.
Create a TestNG class with a WebDriver fixture
A TestNG test class is a Java class containing at least one TestNG annotation. Use @BeforeMethod and @AfterMethod when each test should receive a fresh browser. That prevents cookies, local storage, navigation history, and modified application state from leaking between methods.
package com.example.ui;
import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class LoginTest {
private WebDriver driver;
private WebDriverWait wait;
@BeforeMethod
public void setUp() {
// Make a compatible ChromeDriver available to this process.
driver = new ChromeDriver();
wait = new WebDriverWait(driver, Duration.ofSeconds(10));
}
@Test
public void userCanLogIn() {
driver.get("https://example.test/login");
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("username")))
.sendKeys("user");
driver.findElement(By.id("password")).sendKeys("password");
driver.findElement(By.cssSelector("button[type='submit']")).click();
String heading = wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("h1")))
.getText();
Assert.assertEquals(heading, "Dashboard");
}
@AfterMethod
public void tearDown() {
if (driver != null) {
driver.quit();
}
}
}
The URL, credentials, selectors, and expected heading are illustrative. Use a secret-management mechanism rather than committing real credentials. The null check matters: if browser startup fails in @BeforeMethod, teardown must not throw a second exception while trying to close an uninitialized driver.
Choose the correct lifecycle scope
| Annotation | Use it for | Typical WebDriver implication |
|---|---|---|
@BeforeMethod / @AfterMethod |
Per-test setup and cleanup | Safest isolation; create and quit one driver for every test method. |
@BeforeClass / @AfterClass |
One fixture for all methods in a class | Faster startup, but state can leak unless every test resets it. |
@BeforeTest / @AfterTest |
A TestNG <test> section | Use only when several classes intentionally share a broader fixture. |
@BeforeSuite / @AfterSuite |
The complete suite | Appropriate for suite-wide services, not normally a shared mutable browser. |
TestNG also provides @BeforeGroups and @AfterGroups for group-specific setup and cleanup.
Synchronize dynamic pages with explicit waits
driver.get() waits for a page-load state, but JavaScript can continue changing the DOM afterward. An explicit wait polls for a condition and times out if that condition never becomes true:
Rank #2
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(ExpectedConditions.elementToBeClickable(By.id("save"))).click();
wait.until(ExpectedConditions.textToBePresentInElementLocated(
By.cssSelector(".status"), "Saved"));
This is preferable to fixed sleeps because the test proceeds as soon as the application is ready and reports a timeout at the condition that failed. Selenium’s Java WebDriverWait API uses a Duration and ignores NotFoundException while polling by default.
Implicit, explicit, and fluent waits
- Implicit wait: a global element lookup delay. It is simple but hides timing policy and can make failures harder to diagnose.
- Explicit wait: a targeted condition such as visibility, clickability, URL, title, or text. Prefer this for most dynamic interactions.
- Fluent wait: an explicit wait with customized polling and ignored exceptions when a particular application needs it.
Do not use arbitrary sleeps as the primary synchronization strategy. Wait for the state the user or test actually needs.
Use TestNG annotations and assertions effectively
@Test can annotate a method or class. Its API supports groups, dependencies, data providers, expected exceptions, invocation counts, and enabled flags.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@Test(groups = "smoke")
public class SearchTest {
@DataProvider(name = "queries")
public Object[][] queries() {
return new Object[][] {{"selenium"}, {"testng"}};
}
@Test(dataProvider = "queries")
public void searchReturnsResults(String query) {
// Navigate with WebDriver, wait for results, and assert the rendered state.
}
}
Groups let you select subsets such as smoke or regression. Data providers reuse the same test logic across input sets. Listeners and reporters can add custom reporting or integration hooks; introduce them after the basic lifecycle is reliable.
Run the tests with Maven Surefire
Convention-based discovery
Maven Surefire conventionally discovers classes named *Test.java, *Tests.java, or similar patterns. Run:
mvn test
Keep test classes under src/test/java. A class named LoginTest with a method annotated @Test should be discovered without a suite file.
Run an explicit TestNG suite
Create src/test/resources/testng.xml:
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="UI suite">
<test name="login">
<classes>
<class name="com.example.ui.LoginTest"/>
</classes>
</test>
</suite>
Configure Surefire to use it. Surefire 3.6.0 documentation describes TestNG integration and suite, group, parameter, and parallel configuration; keep the plugin version pinned with the rest of your build.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
</plugins>
</build>
With that configuration, mvn test runs the classes selected by the XML file. Use suite XML when you need a named collection, ordering, groups, parameters, or parallel settings; convention discovery is simpler for a small project.
Parameters, groups, and parallel execution
TestNG parameters let the same suite run against different environments or browser settings. Define them in the suite and accept them in a configuration method:
<suite name="environments">
<parameter name="baseUrl" value="https://staging.example.test"/>
<test name="smoke">
<classes>
<class name="com.example.ui.LoginTest"/>
</classes>
</test>
</suite>
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Parameters;
@Parameters("baseUrl")
@BeforeMethod
public void setUp(String baseUrl) {
driver = new ChromeDriver();
wait = new WebDriverWait(driver, Duration.ofSeconds(10));
driver.get(baseUrl);
}
Only add parallel execution after driver isolation is designed. WebDriver is stateful; sharing one instance between concurrently running tests causes navigation, cookies, and element operations to interfere. Give each parallel worker its own driver and avoid mutable static test state. Start with sequential execution, then configure TestNG’s parallel mode and thread count in suite XML or Surefire once reports are understandable.
Rank #4
Common failures and fixes
Driver cannot be created
Symptom: a driver or session-creation exception in @BeforeMethod. Fix: verify that the browser is installed, the driver is on the process path or otherwise configured, and the browser-driver-Selenium versions are compatible. The null-safe teardown prevents a second failure from obscuring the original one.
Element not found or not clickable
Symptom: NoSuchElementException, ElementNotInteractableException, or an explicit-wait timeout. Fix: confirm the selector against the current DOM, wait for the required state, switch to the correct frame when applicable, and ensure the element is not covered by a modal or consent layer.
Intermittent timeout
Symptom: the same test passes locally but sometimes times out in CI. Fix: replace sleeps with a condition-based wait, wait for network-driven content by its visible result, capture the failing URL and page state in test output, and use a timeout appropriate for the CI environment without making every wait unnecessarily long.
Tests pass alone but fail as a suite
Symptom: order-dependent failures or authentication leakage. Fix: restore state in @AfterMethod, use a fresh driver per method, remove static mutable fixtures, and check whether parallel execution was enabled before isolation was complete.
Maven reports no tests
Symptom: mvn test completes without running the class. Fix: place the class under src/test/java, use a conventional test name, ensure the method or class has @Test, and verify that Surefire’s suite XML or include patterns point to the intended class.
Recommended Free Tools
Best Value
Or skip the browser setup
If your goal is a clean image or PDF of a page rather than an interactive assertion, ScreenshotNeo is a direct HTTP alternative to maintaining browser-capture code. Its API accepts a URL and returns PNG, JPEG, WebP, or PDF; before capture it accepts cookie/consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets. Each cleanup step can be disabled.
One call with cURL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for all parameters. The same request in Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo bills only clean shots. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and each response identifies the result with X-Page-Verdict and X-Billed headers. It also provides an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
Frequently Asked Questions
Why does the null check in tearDown matter?
If browser creation fails in @BeforeMethod, the driver field remains null. Checking it before quit() prevents teardown from throwing a second exception that hides the startup failure.
When should I use suite XML instead of Maven’s default discovery?
Use suite XML when you need named suites, groups, parameters, class selection, or parallel settings. Convention-based discovery is sufficient for a small set of classes named with Maven’s test patterns.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

