ScreenshotNeo

BlogHow-to

How to Create a TestNG XML File for Parallel Testing

Build a valid testng.xml suite, choose the right parallel mode, set thread limits, and run tests safely from the command line.

By the ScreenshotNeo team4 October 20269 min read

A TestNG suite XML file defines which tests to run and how to group them. To run work concurrently, set a parallel mode on the root <suite> element and choose a thread-count. Then put test classes or packages inside one or more <test> elements. The mode determines what TestNG schedules together, so choose it based on which tests can safely share state.

1. Create a minimal parallel suite

Save this as testng.xml in your project. Replace the example class names with fully qualified names for TestNG test classes available on the runtime classpath.

<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="ParallelSuite" parallel="tests" thread-count="4">
  <test name="Regression">
    <classes>
      <class name="com.example.tests.LoginTest"/>
      <class name="com.example.tests.CheckoutTest"/>
    </classes>
  </test>
</suite>

The document has a <suite> root, one or more <test> blocks, and class or package selectors inside each block. Classes listed in the XML should contain TestNG annotations. The parallel setting selects a scheduling mode; thread-count sets the maximum threads used for tests when parallel execution is selected. Setting a thread count alone does not enable parallelism. See the TestNG documentation for the suite structure and options.

2. Choose the parallel mode

Choose the smallest execution unit that meets your speed goal. More concurrency can increase resource use and expose unsafe shared state.

Mode What can run concurrently Grouping behavior Considerations
methods Test methods Methods may run on separate threads; dependency ordering is respected. Highest granularity. Check shared fields, fixtures, browser sessions, and external test data.
tests Separate XML <test> blocks Methods within one <test> run in one thread; separate blocks can run in separate threads. Useful when classes grouped in one block should stay on the same thread.
classes Separate classes Methods of the same class stay in one thread; separate classes can run concurrently. Useful when a class has state shared among its methods but classes are independent.
instances Instances Supported by TestNG; exact behavior should be checked for the project’s TestNG version and instance setup. Use when execution is organized around separate object instances, and verify instance creation and state assumptions.

The documented behavior for the first three modes is described in the TestNG parallel execution documentation. Parallel work can contend for shared mutable fields, browser sessions, files, databases, or fixed test accounts. This is an isolation concern to assess in your suite, not a guarantee that TestNG can make external resources safe.

Example: parallelize test classes

Use classes when methods inside each class should remain together but independent classes may overlap.

<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="ClassParallelSuite" parallel="classes" thread-count="3">
  <test name="UI tests">
    <classes>
      <class name="com.example.tests.LoginTest"/>
      <class name="com.example.tests.CheckoutTest"/>
      <class name="com.example.tests.ProfileTest"/>
    </classes>
  </test>
</suite>

Example: separate groups of tests

Use tests when each XML block is a unit that should remain on one thread. Separate blocks are eligible to run concurrently.

<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="GroupedParallelSuite" parallel="tests" thread-count="2">
  <test name="Account flows">
    <classes>
      <class name="com.example.tests.LoginTest"/>
      <class name="com.example.tests.ProfileTest"/>
    </classes>
  </test>
  <test name="Purchase flows">
    <classes>
      <class name="com.example.tests.CheckoutTest"/>
    </classes>
  </test>
</suite>

3. Select classes or packages

Use <classes> to name specific test classes. Use <packages> to select tests by package when the package is the intended suite boundary. Package scanning is convenient, but review which annotated tests it includes as the project grows.

<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="PackageSuite" parallel="classes" thread-count="4">
  <test name="API package">
    <packages>
      <package name="com.example.tests.api"/>
    </packages>
  </test>
</suite>

Use fully qualified class and package names. A typo or a class missing from the test runtime classpath prevents TestNG from loading the intended suite.

4. Configure thread counts and data providers

thread-count is the suite-level maximum for test execution when a parallel mode is selected. Start with a value your machine and dependencies can handle, then observe failures and resource pressure before increasing it. The -threadcount command-line option sets a default maximum; the suite definition can override it.

Data-provider parallelism is a separate control. Mark a provider with parallel = true to run its data-driven invocations concurrently:

import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class SearchTest {
  @DataProvider(name = "queries", parallel = true)
  public Object[][] queries() {
    return new Object[][] {
      {"alpha"},
      {"beta"},
      {"gamma"}
    };
  }

  @Test(dataProvider = "queries")
  public void searchReturnsResults(String query) {
    // Run an assertion for this query.
  }
}

TestNG documents a default pool size of 10 for each parallel data provider running from an XML file. This is a configuration default, not a performance target. Set data-provider-thread-count when you need to override it:

<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="ProviderSuite" parallel="methods" thread-count="4" data-provider-thread-count="6">
  <test name="Search">
    <classes>
      <class name="com.example.tests.SearchTest"/>
    </classes>
  </test>
</suite>

For TestNG 7.9.0 and later, share-thread-pool-for-data-providers and use-global-thread-pool offer suite-level shared-pool controls. Confirm the TestNG version before adding them. The TestNG parameters documentation recommends the testng-1.1.dtd for IDE completion of these settings; do not copy newer attributes into a project on an older version without checking compatibility. See TestNG Parameters.

5. Run the suite

With TestNG on the classpath, run the XML suite using the documented command:

java org.testng.TestNG testng.xml

Your build tool, IDE, or CI job may define its own dependency and invocation setup. Keep that configuration consistent with the project’s TestNG version and ensure the suite file is passed to the TestNG runner. You can also set a command-line default thread count:

java org.testng.TestNG -threadcount 4 testng.xml

If the XML suite specifies thread-count, that suite setting can override the command-line default. Follow the official command-line documentation for TestNG invocation details.

6. Check isolation before raising concurrency

  • Give each parallel test independent test data, or ensure the data store supports concurrent access.
  • Avoid mutable static fields and shared fixtures unless access is safe across threads.
  • Give browser-based tests their own driver/session per concurrently executing unit and close sessions reliably.
  • Do not make parallel tests depend on a shared fixed account, filename, port, or other exclusive resource.
  • Check that setup and teardown work correctly when tests finish in a different order.
  • Run the suite repeatedly at the intended thread count; intermittent failures often point to shared-state or timing assumptions.

7. Troubleshooting

Symptom Likely cause Fix
Tests run one at a time thread-count is set but parallel is missing, or the chosen mode has only one schedulable unit. Set an appropriate parallel mode and provide multiple methods, classes, or XML <test> blocks for that mode.
Class cannot be loaded The fully qualified name is wrong or the class is absent from the test runtime classpath. Correct the package/class name and confirm the test class is compiled and included in the runner’s classpath.
Suite XML fails to parse Malformed XML, unsupported attributes for the installed version, or a DTD mismatch. Check the root and nested tags, validate attribute spelling, and use documentation matching the installed TestNG version.
Failures appear only in parallel runs Tests may share mutable state, sessions, accounts, or external resources; ordering assumptions may also be exposed. Isolate those resources, choose a coarser parallel mode, or reduce concurrency until the shared dependency is addressed.
Data-provider work exceeds expected concurrency Parallel providers have their own pool setting and documented default, separate from the suite’s test thread count. Set data-provider-thread-count deliberately and, for supported versions, review the shared-pool controls.
New pool attributes are rejected or ignored The project may use a TestNG version older than 7.9.0. Check the dependency version; only use the newer shared-pool attributes where supported.
CI behaves differently from a local run The CI job may invoke a different suite, dependency version, classpath, or command-line thread default. Compare the actual TestNG version and invocation, verify the XML path, and make the thread settings explicit.

8. Performance, reliability, and cost considerations

Parallel execution can reduce elapsed time when tests have independent work, but the XML setting alone cannot predict a speedup. More threads also consume CPU, memory, browser processes, database capacity, and service quota. Excess concurrency can make a suite slower or less reliable through contention. Increase the thread count in measured steps and compare repeatable runs with the same test set and environment.

Use a mode aligned with isolation: for example, classes keeps methods of a class together, while methods exposes more work to concurrency. When a failure is intermittent, first check shared state and external dependencies before assuming the runner is at fault. There is no universal thread count suitable for every project.

9. Capture test pages without managing a browser

For visual evidence from a test target or page, ScreenshotNeo is a website screenshot API and MCP server from ScreenshotNeo. It is separate from TestNG: use TestNG for Java test execution, and call the screenshot endpoint when you need a page image or PDF. See the ScreenshotNeo API documentation for request options.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://stripe.com \
  -o shot.webp

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}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));

Java

Use Java’s built-in HTTP client to make the same GET request and save the response bytes:

import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.charset.StandardCharsets;

public class Screenshot {
  public static void main(String[] args) throws Exception {
    String key = System.getenv("SCREENSHOTNEO_API_KEY");
    if (key == null || key.isBlank()) {
      throw new IllegalStateException("Set SCREENSHOTNEO_API_KEY first");
    }
    String url = "https://stripe.com";
    String query = "access_key=" + URLEncoder.encode(key, StandardCharsets.UTF_8)
        + "&url=" + URLEncoder.encode(url, StandardCharsets.UTF_8);
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.screenshotneo.com/v1/shot?" + query))
        .GET()
        .build();
    HttpResponse<byte[]> response = HttpClient.newHttpClient()
        .send(request, HttpResponse.BodyHandlers.ofByteArray());
    if (response.statusCode() < 200 || response.statusCode() >= 300) {
      throw new IllegalStateException("Screenshot request failed: HTTP " + response.statusCode());
    }
    Files.write(Path.of("shot.webp"), response.body());
  }
}

For production use, check the response status and headers, handle request timeouts and errors, and keep the API key in an environment variable or secret store rather than source control.

Or skip the browser setup

ScreenshotNeo accepts cookie or consent banners like a visitor and removes 60+ known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, and failed loads are never billed, and response headers report the page verdict and billing status. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Every feature is on every plan. Sign up for 1,000 free screenshots a month, with no card.

10. FAQ

Does adding thread-count make a suite parallel?

No. Select a parallel mode as well, and ensure the suite contains multiple schedulable units for that mode.

Can methods that depend on one another run in parallel?

TestNG respects dependency ordering in methods mode. Still review dependencies and shared state when deciding whether the suite is safe to run concurrently.

Should I use the same thread count for tests and data providers?

Not necessarily. Data-provider invocations have a separate pool control, so configure data-provider-thread-count when its default is not appropriate.

Where can I verify version-specific XML options?

Check the official TestNG documentation and parameters reference against the version used by your project.