PC 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 & 11Crashes, 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 minuteBuild it as a normal Java project managed by Maven or Gradle, add Selenium Java and TestNG, create a WebDriver lifecycle around TestNG annotations, select tests in testng.xml, and run the suite through your build tool. Selenium WebDriver controls a browser; TestNG supplies test execution, assertions, grouping, parallel scheduling, and reporting. Keeping those responsibilities separate makes the program easier to run locally and in CI.
What the program contains
A maintainable Selenium TestNG program has five parts:
- A Maven or Gradle build that resolves Selenium and TestNG dependencies.
- A test class whose setup creates a WebDriver, whose
@Testmethods exercise behavior, and whose teardown always quits the browser. - Assertions that decide whether the behavior passed.
- A TestNG suite definition selecting classes, methods, groups, or packages.
- A build-tool command that runs the same suite locally and in continuous integration.
WebDriver is the browser-control layer. It does not determine pass or fail; TestNG runs the methods and evaluates their assertions.
Create the Java project
Maven layout
Create this directory structure:
selenium-testng-demo/
├── pom.xml
├── testng.xml
└── src/
└── test/
└── java/
└── example/
└── SearchTest.java
Put test sources under src/test/java. Keep testng.xml at the project root (or under src/test/resources if your build points to that path).
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemspom.xml
The dependency coordinates are Selenium’s Java binding and TestNG. Pin the versions your team has approved; do not leave a production build on an unreviewed moving version.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>example</groupId>
<artifactId>selenium-testng-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<selenium.version>SET_APPROVED_SELENIUM_VERSION</selenium.version>
<testng.version>SET_APPROVED_TESTNG_VERSION</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>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.2</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
</plugins>
</build>
</project>
Replace the two version properties with the releases selected by your organization. Committing those values makes a local run and a CI run resolve the same libraries.
Gradle alternative
Gradle’s TestNG runner is equivalent. In build.gradle, add the two dependencies with pinned versions and configure the test task:
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.seleniumhq.selenium:selenium-java:PINNED_VERSION'
testImplementation 'org.testng:testng:PINNED_VERSION'
}
test {
useTestNG() {
suites 'testng.xml'
}
}
Use one build system per repository unless there is a clear reason to maintain both.
Free tools Windows power users keep installed
One-click scans. No signup required.
Write a WebDriver test with TestNG
This example uses TestNG’s per-method lifecycle, so every test starts with a fresh browser and ends by quitting it.
package example;
import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class SearchTest {
private WebDriver driver;
@BeforeMethod
public void setUp() {
driver = new ChromeDriver();
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
driver.manage().window().maximize();
}
@Test(groups = {"smoke"})
public void pageTitleContainsExample() {
driver.get("https://example.com/");
String heading = driver.findElement(By.cssSelector("h1")).getText();
Assert.assertEquals(heading, "Example Domain");
}
@AfterMethod(alwaysRun = true)
public void tearDown() {
if (driver != null) {
driver.quit();
}
}
}
Why each annotation is there
@BeforeMethodruns before each test method. Use@BeforeClasswhen a class-level fixture is genuinely safe to share.@Testmarks executable behavior. Put assertions in the test, not in page-object constructors.@AfterMethod(alwaysRun = true)runs even after a failure, preventing orphaned browser processes.quit()closes every window and ends the driver session;close()only closes the current window.
Prefer explicit waits for dynamic elements over a large implicit wait. For example:
Rank #2
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement button = wait.until(
ExpectedConditions.elementToBeClickable(By.id("submit")));
button.click();
Configure testng.xml
A suite can contain one or more <test> elements, and each test can include classes, methods, groups, or packages. This file runs the example class and passes a browser parameter:
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Web smoke suite" verbose="1">
<parameter name="browser" value="chrome"/>
<test name="Smoke tests">
<groups>
<run>
<include name="smoke"/>
</run>
</groups>
<classes>
<class name="example.SearchTest"/>
</classes>
</test>
</suite>
If you use the parameter, receive it with @Parameters("browser") in setup and create the matching driver. Otherwise remove the parameter from the XML. Keep the suite file in source control so developers and CI select the same tests.
Other selection patterns
- Methods: add
<methods><include name="pageTitleContainsExample"/></methods>inside a class to run only named methods. - Groups: include or exclude groups such as
smoke,regression, orapi. - Packages: use
<packages><package name="example"/></packages>for a package-wide selection.
Run the suite
- Install a supported JDK and put its
javaexecutable onPATH. - From the directory containing
pom.xml, runmvn test. Surefire readstestng.xmland writes results undertarget/surefire-reports. - To run one suite explicitly, use
mvn -Dsurefire.suiteXmlFiles=testng.xml test. - With Gradle, run
./gradlew test; reports are underbuild/reports/tests/test.
Run a single Maven class with mvn -Dtest=example.SearchTest test, but remember that a class filter can bypass the groups and parameters you defined in the suite file.
Do you need to install ChromeDriver manually?
Usually, no. Selenium Manager can discover, download, and cache the required driver and, where supported, the browser. Its documented cache location is ~/.cache/selenium. Starting new ChromeDriver() allows Selenium to invoke that management automatically.
Manual management is still reasonable when a locked-down CI image cannot download binaries, when your organization mirrors drivers internally, or when browser versions must be frozen exactly. In those cases, install the driver on the image, put it on PATH, or configure the appropriate driver service. Check the browser/driver pair whenever a session fails to start; a mismatch commonly produces a session-creation error.
Run tests in parallel with TestNG
Parallelism shortens wall-clock time but is safe only when each concurrent test owns its browser and its test data. Do not store a mutable WebDriver in a static field shared by threads.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Parallel classes
Change the suite declaration:
<suite name="Parallel suite" parallel="classes" thread-count="3">
<test name="UI tests">
<classes>
<class name="example.SearchTest"/>
<class name="example.CheckoutTest"/>
<class name="example.ProfileTest"/>
</classes>
</test>
</suite>
TestNG also supports parallel="methods", parallel="tests", and parallel="instances". A parallel data provider can feed multiple data sets concurrently. Choose the narrowest mode that matches your isolation model.
Thread-local driver pattern
If a framework layer must expose a driver across helper classes, use a ThreadLocal<WebDriver>, initialize it before each test, and remove it after quitting. Even then, isolate accounts, files, records, and other server-side data; thread-local browsers do not prevent two tests from editing the same account.
Move to Selenium Grid
Grid is the next step when browsers should run on another machine, across operating systems, or concurrently on dedicated nodes. Start a Selenium Server in standalone mode, then point the test at its remote endpoint:
java -jar selenium-server.jar standalone
WebDriver driver = new RemoteWebDriver(
URI.create("http://localhost:4444").toURL(),
new ChromeOptions());
Keep the same TestNG lifecycle; only driver construction changes. For CI, record the server version, browser version, node capacity, screen size, and artifacts (logs, screenshots, and page source) so a remote failure can be reproduced.
Reliability, speed, and cost decisions
Make failures diagnosable
- Capture a screenshot and page source in an
@AfterMethodlistener when a test fails. - Use stable IDs or accessible attributes instead of brittle XPath tied to layout.
- Wait for a specific state (visible, enabled, URL, or text) rather than sleeping for a fixed number of seconds.
- Keep test data disposable and resettable; never depend on execution order.
Control runtime
- Use headless mode only when the application behaves identically without a visible display; keep a headed option for debugging.
- Run a small smoke group on every change and the larger regression group on a scheduled or gated job.
- Increase TestNG’s thread count only after measuring node capacity, application rate limits, and test-data contention. There is no universal safe number.
Budget infrastructure
Local execution costs developer CPU and memory. Grid adds server and browser-node administration, but gives you remote environments and concurrency. Selenium and TestNG themselves do not provide a hosted browser fleet; that infrastructure is a separate operational decision.
Or skip the browser setup
If your task is to obtain a clean page image or PDF rather than interactively test behavior, ScreenshotNeo is the first screenshot API to try: it removes consent banners, popups, and chat widgets before capture, and bills only clean shots.
One request is enough (see the ScreenshotNeo API documentation):
Rank #4
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
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 can also capture full pages with lazy images, a CSS-selected element, dark mode and device presets; apply custom CSS or JavaScript; click or hide elements; wait for a selector, delay, or network idle; block ads, trackers, requests, or resource types; set headers, cookies, user agent, authorization, timezone, and geolocation; produce PDFs; resize images; cache with a chosen TTL; create signed links; submit asynchronous jobs with signed webhooks; capture up to 100 URLs per bulk call; and expose usage and OpenAPI endpoints. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.
Recommended Free Tools
Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Troubleshooting
“Unable to create a new session”
Check that the browser is installed, the process can download or locate a driver, and the browser/driver versions are compatible. In restricted CI, preinstall and pin both.
The test hangs at an element lookup
Replace an implicit wait or sleep with an explicit wait for the element’s actual condition. Confirm the locator targets the current frame and that the page has finished navigation.
Tests pass alone but fail in parallel
Look for shared static drivers, shared accounts, fixed filenames, ports, or database rows. Create those resources per test or protect them with controlled fixtures; reducing thread-count is a diagnostic, not a complete fix.
Maven runs no tests
Verify the suite path in Surefire, the class name in testng.xml, and the conventional src/test/java location. Run Maven with the class filter to distinguish discovery problems from suite-selection problems.
Best Value
Grid reports connection refused
Confirm the Selenium Server process is running, port 4444 is reachable from the test machine, and the URL includes the correct scheme and path. A local browser driver cannot compensate for an unavailable remote server.
FAQ
Can TestNG replace Selenium?
No. TestNG schedules and evaluates tests; Selenium WebDriver drives the browser. A UI test normally needs both.
Is testng.xml mandatory?
No. Maven, Gradle, annotations, and build-tool filters can select tests without it. A suite file is valuable when you need a repeatable collection, groups, parameters, or parallel settings.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Should every test create a new browser?
For isolation, a new driver per test method is the safest default. Share a session only when the scenario intentionally models a sequence and the resulting coupling is acceptable.
When should a team use Grid?
Use Grid when local execution cannot provide the required browser/OS coverage, remote execution, or concurrency. Start locally, then move the same WebDriver code to RemoteWebDriver as infrastructure becomes available.
Frequently Asked Questions
Can TestNG replace Selenium?
No. TestNG schedules and evaluates tests; Selenium WebDriver drives the browser. A UI test normally needs both.
Is testng.xml mandatory?
No. Maven, Gradle, annotations, and build-tool filters can select tests without it. A suite file is useful for repeatable groups, parameters, and parallel settings.
Should every test create a new browser?
A new driver per test method is the safest isolation default. Share a session only for an intentionally sequential scenario.
When should a team use Grid?
Use Grid when local execution cannot provide the required browser/OS coverage, remote execution, or concurrency.
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.

