Companion code for the IT Talk "JUnit 6 vs TestNG 7". Previous edition: TestNG vs. JUnit 4 slides Β· TestNG vs. JUnit 4 webinar
Related projects:
- π§ͺ JUnit Workshop β companion JUnit 6 examples to compare side-by-side with this repo
- π Selenium Example β real-world Selenium WebDriver framework built on top of TestNG
Hands-on examples for TestNG 7 in Java β covering every major feature from basic assertions to advanced suite orchestration, listeners, data providers, retry strategies, and parallel execution.
Use it as a practical reference, a workshop starter kit, or a side-by-side comparison with the companion JUnit Workshop.
- Found it useful?
- Why this repository?
- Who Is This For?
- Quick Start
- Prerequisites
- Supported Versions
- Feature Map
- Learning Path β Beginners
- Advanced Topics β Path for Senior Engineers
- Command Examples
- CI / CD
- AI Assistant Support
- Project Structure
- Additional Resources
- Useful Links
- License
If you found useful examples or information in this repository, please give it a β
Your support helps the project reach more Java and QA engineers.
Most TestNG tutorials stop at annotations and @DataProvider.
This project goes further β it covers the full TestNG 7 feature set with real, runnable code you can clone, execute, and adapt immediately.
Use this repository if you want to:
- learn TestNG from scratch with a structured, step-by-step learning path
- see real runnable examples instead of reading documentation only
- explore advanced features β listeners (
ITestListener,ISuiteListener,IExecutionListener), retry strategies, parallel execution, XML suites, groups, and data providers - understand suite orchestration β
@BeforeSuite,@BeforeTest,ISuiteListener, and when to use each - compare TestNG and JUnit patterns and idioms side-by-side with the companion JUnit Workshop
- use it as material for workshops, tech talks, onboarding sessions, and self-study
| Audience | What you will get |
|---|---|
| QA engineers new to TestNG | A guided tour of every major feature with runnable examples |
| Java developers migrating from JUnit 4 / JUnit 5+ | Side-by-side comparison of patterns and idioms |
| Senior / lead engineers | Deep-dives into listeners, retry strategies, parallel execution, and groups |
| Workshop facilitators | A ready-made project you can hand to attendees |
git clone https://github.com/a-oleynik/testng-workshop.git
cd testng-workshop
mvn clean testπ‘ No local Maven? Use the included Maven Wrapper instead:
./mvnw clean test # Linux / macOS mvnw.cmd clean test # Windows
| Tool | Minimum version | Notes |
|---|---|---|
| JDK | 21 LTS | |
| Maven | 3.9+ (optional but recommended) | Not required if using the included Maven Wrapper (mvnw) |
| IDE | Any (IntelliJ IDEA recommended) | Lombok plugin required for IDE support |
| Lombok plugin | Latest | IntelliJ: Settings β Plugins β Lombok |
π‘ Maven Wrapper included β this project ships with
mvnw(Linux/macOS) andmvnw.cmd(Windows).
You can use it instead of a locally installed Maven. The wrapper automatically downloads the correct Maven version on first run.# Linux / macOS ./mvnw clean test # Windows mvnw.cmd clean test
| Library | Version used |
|---|---|
| TestNG | 7.12.0 |
| AssertJ | 3.27.7 |
| Hamcrest | 3.0 |
| Lombok | 1.18.46 |
| OpenCSV | 5.12.0 |
| SLF4J | 2.0.17 |
| Maven Surefire Plugin | 3.5.6 |
Java --release |
21 |
π‘ New to TestNG? Follow the Learning Path for a recommended study order.
| Package / folder | Feature demonstrated | Test class(es) |
|---|---|---|
general |
Basic assertions (assertEquals, assertTrue, assertNull, assertSame, β¦) |
AssertTest |
general |
Exception testing (expectedExceptions, expectedExceptionsMessageRegExp) |
ExceptionTest |
general |
Test fixtures (@BeforeSuite, @AfterSuite, @BeforeClass, @AfterClass, @BeforeTest, @AfterTest, @BeforeMethod, @AfterMethod) |
FixturesTest |
general |
Hamcrest matchers | HamcrestTest |
general |
Timeouts (timeOut attribute on @Test) |
TimeoutTest |
general |
Disabling & skipping tests (enabled = false, SkipException) |
DisabledTest |
general |
Test descriptions (description attribute on @Test) |
DescriptionTest |
group/asserts |
Soft assertions (SoftAssert) |
SoftAssertTest |
conditional |
Assumptions via AssertJ (assumeThat) per method |
AssumeTest |
conditional |
Assumptions in @BeforeClass to skip entire class |
AssumeBeforeClassTest |
ddt |
Static inline @DataProvider |
StaticDataProviderTest |
ddt |
Dynamic CSV-file @DataProvider (Iterator-based) |
DynamicDataProviderTest |
ddt |
External @DataProvider class (dataProviderClass) |
OuterDataProviderTest |
nested |
Inner test classes grouped under a parent class | NestedTest |
grouping |
Groups (@Test(groups = β¦)), @BeforeGroups, @AfterGroups |
GroupTest |
dependencies |
Method & group dependencies (dependsOnMethods, dependsOnGroups) |
DependenciesTest |
execution/order |
Test execution ordering via priority attribute |
ExecutionOrderTest |
suites |
Suite lifecycle annotations (@BeforeSuite, @AfterSuite) |
SuiteAnnotationsTest |
suites |
Test-block lifecycle annotations (@BeforeTest, @AfterTest) |
TestAnnotationsTest |
suites |
ISuiteListener β suite-scoped setup/teardown via listener |
SuiteListenerTest |
suites |
IExecutionListener β process-wide execution lifecycle (XML registration only) |
ExecutionListenerTest |
listeners |
ITestListener β observe test start, success, failure, skip |
TestListenerTest |
listeners |
IInvokedMethodListener β intercept before/after every method invocation |
MethodListenerTest |
listeners |
TestListenerAdapter β extend the adapter and override only the callbacks you need |
TestListenerTest |
retry |
Retry with IRetryAnalyzer + custom @Retries annotation |
RetryTest |
retry |
Retry with IAnnotationTransformer (RetryTransformer) applied globally via XML |
RetryTransformerTest |
repeat |
Repeated execution (invocationCount attribute on @Test) |
RepeatedTest |
Work through these topics in order; each builds on the previous one.
-
Basic assertions β
AssertTest
LearnassertEquals,assertTrue,assertNull,assertSame,assertNotSame, andfail. -
Test lifecycle β
FixturesTest
Understand the full TestNG fixture hierarchy:@BeforeSuiteβ@BeforeTestβ@BeforeClassβ@BeforeMethod(and theirAfter*counterparts). -
Exception testing β
ExceptionTest
UseexpectedExceptionsandexpectedExceptionsMessageRegExpon@Testto assert thrown exceptions. -
Disabling & skipping tests β
DisabledTest
Disable tests with@Test(enabled = false)or skip them dynamically by throwingSkipException. -
Test descriptions β
DescriptionTest
Add human-readable descriptions to tests with@Test(description = "β¦")for better report output. -
Hamcrest matchers β
HamcrestTest
Write expressive assertions withassertThat,equalTo,is,closeTo,bothβ¦and, and more. -
Soft assertions β
SoftAssertTest
Collect all assertion failures withSoftAssertbefore reporting β no early bail-out. -
Assumptions β
AssumeTest
Skip tests dynamically when preconditions aren't met using AssertJ'sassumeThat. -
Parameterized tests β
StaticDataProviderTest
Drive one test method with multiple data rows using an inline@DataProvider. -
Groups β
GroupTest
Mark tests asSmokeorRegressionand run subsets from the command line or via XML suites. -
Suite & test-block lifecycle β
SuiteAnnotationsTest,TestAnnotationsTest
See@BeforeSuite/@AfterSuiterun once around the entire suite, and@BeforeTest/@AfterTestrun once per<test>block in atestng.xmlfile. Having seen XML suites in step 10, the distinction between@BeforeTestand@BeforeClassnow makes full sense β and this topic bridges directly into the Advanced Topics section.
Run the whole beginner suite:
mvn clean testThese topics assume familiarity with TestNG basics.
SuiteAnnotationsTest, TestAnnotationsTest, SuiteListenerTest, ExecutionListenerTest β suites/ package
TestNG provides three strategies for running code at suite scope, from simplest to most powerful:
| Strategy | Class | When to use |
|---|---|---|
@BeforeSuite / @AfterSuite |
SuiteAnnotationsTest |
Simplest option; lives inside a test class |
ISuiteListener |
SuiteListenerTest + MySuiteListener |
Infrastructure code outside the test hierarchy; fires per suite |
IExecutionListener |
ExecutionListenerTest + MyExecutionListener |
Process-wide hook across all suites; must be registered in XML |
Full fixture execution order:
IExecutionListener.onExecutionStart() β once per JVM run
ISuiteListener.onStart(suite) β once per suite
@BeforeSuite β annotation-based, once per suite
@BeforeTest β once per <test> block in testng.xml
@BeforeClass β once per test class
@BeforeMethod / @Test / @AfterMethod
@AfterClass
@AfterTest
@AfterSuite
ISuiteListener.onFinish(suite)
IExecutionListener.onExecutionFinish()
JUnit 5 equivalent:
@BeforeAll/@AfterAll(annotation-based) or a custom extension implementingBeforeAllCallback/AfterAllCallback(listener-based).
@BeforeTest / @AfterTest (TestAnnotationsTest) fire once per <test> XML element β coarser than @BeforeClass but finer than @BeforeSuite. They are TestNG-specific and have no direct JUnit 5 counterpart.
IExecutionListener cannot be registered via @Listeners on a test class β it must be in the <listeners> block of a testng.xml:
<suite name="SuitesDemo" parallel="none">
<listeners>
<listener class-name="com.oleynik.qa.workshop.testng.suites.MySuiteListener"/>
<listener class-name="com.oleynik.qa.workshop.testng.suites.MyExecutionListener"/>
</listeners>
<!-- ... -->
</suite>Run the full suites demo:
mvn clean test -Dsurefire.suiteXmlFiles=src/test/resources/testngsuites.xmlGroupTest β grouping/ package
Assign tests to named groups. Use @BeforeGroups and @AfterGroups for group-scoped setup/teardown.
Run groups from the command line:
mvn clean test -Dgroups=Regression,SmokeOr target a specific XML suite that filters by group:
# Run only Smoke tests via XML
mvn clean test -Dsurefire.suiteXmlFiles=src/test/resources/testnggroups2.xml
# Run groups-of-groups (Smoke + Regression + Fail) via XML
mvn clean test -Dsurefire.suiteXmlFiles=src/test/resources/testnggroups1.xmlDependenciesTest β dependencies/ package
Chain tests so that a downstream test is skipped (not failed) when its upstream dependency fails, using dependsOnMethods or dependsOnGroups.
DynamicDataProviderTest, OuterDataProviderTest β ddt/ package
- Load test data lazily from CSV files via an
Iterator-based@DataProvider. Test data lives insrc/test/resources/numbers.csv. - Separate data-provider logic into a dedicated
MyDataProviderclass and reference it withdataProviderClass. - Use the custom
@DataSource(path = "β¦")annotation to bind a CSV file path to a test method at runtime.
NestedTest β nested/ package
Group related scenarios (Multiply, Divide, Add) as public inner classes inside a parent class. Run them via testnested.xml:
mvn clean test -Dsurefire.suiteXmlFiles=src/test/resources/testnested.xmlExecutionOrderTest β execution/order/ package
Control method execution order with the priority attribute on @Test (lower value = earlier execution). Methods without a priority run before prioritized ones.
TestListenerTest, MethodListenerTest β listeners/ package
ITestListener(MyTestListener) β react to test start, success, failure, and skip events. Ideal for screenshot capture on failure.IInvokedMethodListener(MyMethodListener) β intercept every method invocation (including@Before*/@After*).TestListenerAdapter(MyExtendedTestListener) β extend the adapter and override only the callbacks you need.
Attach listeners declaratively with@Listeners(value = MyTestListener.class)or globally via atestng.xml<listeners>block.
RetryTest β IRetryAnalyzer + custom @Retries(limit = N) annotation applied per test method
RetryTransformerTest β IAnnotationTransformer (RetryTransformer) injects RetryAnalyzer globally for every test in the suite without touching test source code
Run the transformer-based retry suite:
mvn clean test -Dsurefire.suiteXmlFiles=src/test/resources/testngretrylistener.xmlConfigured globally in pom.xml via Surefire:
<parallel>methods</parallel>
<threadCount>3</threadCount>Tests run concurrently by default. Fine-tune parallelism per suite with parallel="classes" or parallel="tests" and a custom thread-count in any testng.xml. For example, testng2.xml runs a fixed set of classes in parallel with thread-count="2":
mvn clean test -Dsurefire.suiteXmlFiles=src/test/resources/testng2.xmlAll suite XML files live in src/test/resources/. They demonstrate:
| File | What it shows |
|---|---|
testng1.xml |
Include / exclude specific test methods per class |
testng2.xml |
Run a fixed set of classes in parallel |
testnggroups1.xml |
Groups-of-groups β combine named groups under one alias |
testnggroups2.xml |
Include only the Smoke group |
testngretrylistener.xml |
Register RetryTransformer listener globally |
testnested.xml |
Run all tests from a package (including inner classes) |
testngsuites.xml |
ISuiteListener + IExecutionListener + suite lifecycle annotations |
Run any suite directly:
mvn clean test -Dsurefire.suiteXmlFiles=src/test/resources/testng1.xmlmvn clean surefire-report:report
# or
mvn clean siteReports are written to
target/site/surefire-report.html
mvn clean testReports are written to:
- Surefire report:
target/surefire-reports/index.html- TestNG emailable report:
target/surefire-reports/emailable-report.html
mvn clean test -Dtest=AssertTestmvn clean test -Dtest=AssertTest#assert_equals_multiplication_testmvn clean test -Dtest=AssertTest,HamcrestTestmvn clean test -Dtest=AssertTest#assert_equals*mvn clean test -Dtest=AssertTest#assert_equals*+assert_boolean*mvn clean test -Dgroups=Regression,Smokemvn clean test -Dsurefire.suiteXmlFiles=src/test/resources/testng1.xmlmvn clean installmvn clean install -DskipTestsmvn clean surefire-report:reportSurefire report is written to:
- Surefire report:
target/reports/surefire.html
mvn clean test -XThis project uses GitHub Actions with a manually triggered workflow.
Triggered manually from Actions β Run workflow on GitHub.
| Input | Required | Description |
|---|---|---|
groups |
No | Group filter for the by-group job (e.g. Smoke, Regression). Leave empty to skip that job. |
| Job | Name | Runs when | Command |
|---|---|---|---|
regression |
Regression β all tests | Always | ./mvnw -B clean test |
by-group |
By group β {groups} |
Only when groups input is filled in |
./mvnw -B clean test -Dgroups={groups} |
Each job uploads two artifacts after completion β including on failure (if: always()):
| Artifact | Source path | Contents |
|---|---|---|
testng-reports / testng-reports-{groups} |
target/surefire-reports/ |
Full HTML report β open index.html in a browser |
junit-xml-results / junit-xml-results-{groups} |
target/surefire-reports/ |
Raw JUnit XML files β compatible with CI dashboards and report parsers |
- Go to the Actions tab on GitHub
- Select Java CI with Maven in the left panel
- Click Run workflow
- Optionally fill in the
groupsfield (e.g.Smoke) to also run the by-group job - Click Run workflow
- Click the completed workflow run
- Scroll to Artifacts β download
testng-reports(HTML) orjunit-xml-results(XML) - Open
index.html(from the TestNG report) in a browser
# All tests
mvn clean test
# By group
mvn clean test -Dgroups=Smoke
mvn clean test -Dgroups=RegressionThis project ships configuration files for AI coding assistants so they have full context about the tech stack, package layout, coding conventions, and TestNG-specific rules.
| Assistant | File | Description |
|---|---|---|
| GitHub Copilot (chat / completions / agent mode) | .github/copilot-instructions.md |
Always-on workspace instructions β project overview, conventions, key patterns, task recipes, what NOT to do |
| JetBrains Junie | .junie/guidelines.md |
Project guidelines β full source layout, naming rules, threading constraints, learning path |
| OpenAI Codex / Claude Code / Gemini CLI | AGENTS.md |
Agentic guidance β build & verify commands, feature index, common patterns, important rules |
All files cover:
- Full tech stack and versions
src/mainandsrc/testpackage breakdown with class inventory- Naming conventions (
snake_casetest methods,PascalCaseclasses) - TestNG-specific rules (e.g.
IExecutionListenermust be registered in XML, not via@Listeners) - Thread-safety constraints (
parallel=methods,threadCount=3enabled by default) - All run commands including Maven Wrapper
src/
βββ main/java/com/oleynik/qa/workshop/testng/
β βββ model/ # Domain model (User, MyDoubleWrapper)
β βββ annotations/ # Custom annotations (@Retries, @DataSource)
β βββ dataproviders/ # Reusable data provider (MyDataProvider)
β βββ listeners/ # ITestListener, IInvokedMethodListener, IAnnotationTransformer (RetryTransformer)
β βββ suites/ # ISuiteListener (MySuiteListener), IExecutionListener (MyExecutionListener)
β βββ retry/ # IRetryAnalyzer implementation (RetryAnalyzer)
β βββ Factorial.java # Utility class used by data-driven tests
β βββ Utils.java # Shared test utilities (e.g. waitFor)
βββ test/java/com/oleynik/qa/workshop/testng/
βββ general/ # Core assertions, fixtures, exceptions, descriptions, timeouts
βββ group/asserts/ # Soft assertions
βββ conditional/ # Assumptions (AssertJ assumeThat)
βββ ddt/ # Data-driven tests (@DataProvider, CSV, external provider)
βββ nested/ # Inner-class test grouping
βββ grouping/ # TestNG groups (@Test(groups = β¦))
βββ dependencies/ # Test dependencies (dependsOnMethods / dependsOnGroups)
βββ execution/order/ # Test execution ordering (priority)
βββ listeners/ # Tests demonstrating listener hooks
βββ suites/ # Suite lifecycle: @BeforeSuite/@AfterSuite, @BeforeTest/@AfterTest,
β # ISuiteListener, IExecutionListener
βββ retry/ # Retry strategies (IRetryAnalyzer, IAnnotationTransformer)
βββ repeat/ # Repeated execution (invocationCount)
- TestNG Documentation
- TestNG GitHub
- AssertJ Documentation
- Hamcrest Tutorial
- Lombok Features
- OpenCSV Documentation
- Maven Surefire Plugin
- Maven Surefire Report Plugin
- Maven Wrapper β run Maven without a local installation
- JUnit Workshop β companion JUnit 6 examples (there are also branches with JUnit 5 & 4)
- Selenium Example β real-world Selenium framework using TestNG
- TestNG XML Is a Legacy Concept: Here's What Modern Test Suites Should Look Like β article on modern TestNG suite configuration
- Java Download
- Maven Download
- TestNG Releases
- JUnit 6 Released β Clean-Up, Modernization & Minimal Disruption
- Lombok Download
- IntelliJ Lombok Plugin
This project is licensed under the MIT License β see the LICENSE file for details.