Setting Up TestNG with Maven

8 min read

You already set up Maven with Selenium and TestNG in the Selenium course — this lesson revisits that setup with TestNG as the centrepiece rather than an afterthought. When Selenium is the main event, you throw in TestNG as a test runner and move on. When TestNG is the focus, you configure it deliberately: the right Surefire version, a testng.xml that actually drives the run, and a project layout that scales from one test class to five hundred. This lesson builds that foundation and explains why each piece exists rather than just telling you what to type.

The pom.xml — what you actually need

For a pure TestNG project (no Selenium yet):

<project xmlns="http://maven.apache.org/POM/4.0.0">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mycompany.tests</groupId>
    <artifactId>testng-suite</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>
    </properties>
 
    <dependencies>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>7.10.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.2.5</version>
                <configuration>
                    <suiteXmlFiles>
                        <suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile>
                    </suiteXmlFiles>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Three things worth understanding here, not just copying:

<scope>test</scope> on TestNG. This scopes the dependency to the test compile and test runtime classpaths only. TestNG never ends up in your production JAR. This isn't just cleanliness — it's a correctness boundary. If you accidentally run application code from a test method, the test scope prevents production from depending on test infrastructure.

maven-surefire-plugin version. Maven ships with a bundled Surefire version that is often outdated. By declaring the plugin explicitly with version 3.2.5 you get correct TestNG 7 support, better parallel test handling, and accurate JUnit/TestNG detection. Always pin it.

<suiteXmlFile>. This tells Surefire to hand control to testng.xml entirely. Without this, Surefire falls back to its own test-discovery rules (scanning for classes named *Test.java), which ignores all your TestNG group and parameter configuration. Once you have a testng.xml, always point Surefire at it.

Project layout

testng-suite/
├── pom.xml
└── src/
    └── test/
        ├── java/
        │   └── com/mycompany/tests/
        │       ├── base/          ← BaseTest.java
        │       └── tests/         ← @Test classes
        └── resources/
            ├── testng.xml         ← suite descriptor
            └── testdata/          ← Excel, CSV, JSON files

No src/main/java needed — this is a pure test project. The base/ package holds your BaseTest class with shared setup/teardown that all test classes extend. The tests/ package holds the actual @Test classes. testdata/ holds external data files your @DataProvider methods will read in chapter 3.

A minimal testng.xml to start with

<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="TestNG Suite" verbose="1">
    <test name="All Tests">
        <packages>
            <package name="com.mycompany.tests.tests"/>
        </packages>
    </test>
</suite>

verbose="1" prints each test name as it runs — helpful during development to see what's executing. Use verbose="0" in CI to keep logs clean. <packages> auto-discovers every @Test class in that package — you never have to register individual class names. Add a new test class and it runs on the next mvn test automatically.

A verification test class

package com.mycompany.tests.tests;
 
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
 
public class SetupVerificationTest {
 
    @BeforeMethod
    public void setup() {
        System.out.println("Setup: ready to run test");
    }
 
    @Test
    public void testngIsConfiguredCorrectly() {
        Assert.assertTrue(true, "If you see this pass, TestNG is wired up correctly");
    }
 
    @Test
    public void assertionsWork() {
        Assert.assertEquals("testng", "testng", "String equality works");
        Assert.assertNotNull("some value", "Not-null check works");
    }
 
    @AfterMethod
    public void teardown() {
        System.out.println("Teardown: test complete");
    }
}

Run mvn clean test. You should see:

Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS

If you see Tests run: 0, the most common cause is that Surefire can't find your testng.xml — double-check the path in <suiteXmlFile>.

Running tests — three ways

# Run everything via testng.xml (standard)
mvn clean test
 
# Run a single class directly (bypasses testng.xml)
mvn test -Dtest=SetupVerificationTest
 
# Run tests in a specific group (only works if testng.xml has group config)
mvn test -Dgroups=smoke
 
# Pass a different suite file without editing pom.xml
mvn test -DsuiteXmlFile=src/test/resources/smoke.xml

In IntelliJ: right-click any @Test method → Run. Right-click the class → Run. Right-click testng.xml → Run. The IntelliJ TestNG plugin is built in — no installation required.

The full setup flow

Step 1 of 6

Create Maven project

IntelliJ → File → New Project → Maven. GroupId: com.mycompany.tests, ArtifactId: testng-suite. Alternatively: mvn archetype:generate. Both produce the same pom.xml skeleton.

Surefire output and reports

After mvn test, Surefire writes two sets of output:

  • Console: test names and pass/fail status
  • target/surefire-reports/: XML and TXT results for each class — the input for CI tools like Jenkins
  • test-output/: TestNG's own HTML report — open test-output/index.html in a browser for the full report with method-level details

The test-output/ directory is generated by TestNG, not Maven. If you're running tests via IntelliJ directly (not mvn test), you'll find it in the project root rather than target/. Add test-output/ and target/ to your .gitignore.

⚠️ Common mistakes

  • Missing <scope>test</scope>. Without it, TestNG lands on your production classpath. It compiles, everything appears to work, but you've shipped a test framework into your production artefact. Surefire also behaves differently when TestNG is on the compile scope vs test scope — always scope it correctly.
  • Outdated Surefire. Maven's bundled Surefire is often 2.22.x, which has known issues with TestNG 7 parallel execution and data providers. Always declare Surefire 3.2.5 or higher explicitly — one line in <plugins> prevents hours of mysterious failures.
  • Surefire pointing at the wrong XML path. <suiteXmlFile>testng.xml</suiteXmlFile> is evaluated relative to the project root, not src/test/resources/. If you put the file in src/test/resources/ you must write src/test/resources/testng.xml. Check this first when you see Tests run: 0 — it's the most common root cause.

🎯 Practice task

Build the TestNG project skeleton you'll use for this entire course. 25–30 minutes.

  1. Create a new Maven project testng-suite with GroupId com.mycompany.tests. Do not reuse your Selenium project — this keeps the courses cleanly separated.
  2. Add the TestNG dependency and Surefire plugin as shown. Run mvn dependency:tree and confirm testng-7.10.2 appears in the output.
  3. Create the package tree and testng.xml. Run mvn clean test — expect Tests run: 0, BUILD SUCCESS (no test classes yet).
  4. Add SetupVerificationTest.java. Run mvn clean test. Confirm Tests run: 2.
  5. Force a failure. Change one assertion to Assert.assertEquals(1, 2). Run mvn test. Read the failure output — note how TestNG reports the expected vs actual values and which method failed. Revert.
  6. Open the HTML report. After a passing run, open test-output/index.html in a browser. Explore the suite summary and the per-method detail. You'll be reading this report regularly throughout the course.
  7. Stretch. Run mvn test -Dtest=SetupVerificationTest#testngIsConfiguredCorrectly — the #methodName syntax runs a single method. Useful for focused debugging.

Next lesson: the complete TestNG annotation model. All ten lifecycle annotations, their execution order, and the @Test attributes that control individual test behaviour.

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