TestNG is tightly coupled with Selenium in interviews. Most interviewers ask TestNG questions right after Selenium. The key is not just knowing what annotations do, but explaining how you configured them in your framework and why.
Q: What is the execution order of TestNG annotations?
A: @BeforeSuite (once for entire suite) > @BeforeTest (before each <test> tag in testng.xml) > @BeforeClass (once before first method in class) > @BeforeMethod (before each @Test) > @Test > @AfterMethod > @AfterClass > @AfterTest > @AfterSuite. In my framework: @BeforeSuite loads configuration and initializes the report. @BeforeMethod launches the browser using BrowserFactory and navigates to the base URL. @Test contains the actual test steps and assertions. @AfterMethod takes a screenshot if the test failed and calls driver.quit(). @AfterSuite sends the test report email. Common follow-up: "Why not launch the browser in @BeforeClass?" — Because each @Test method gets a fresh browser, eliminating state leakage between tests. If one test corrupts the session, the next test is unaffected.
Q: What is a DataProvider? How is it different from @Parameters?
A: DataProvider is a method annotated with @DataProvider that returns Object[][] — each row is a set of parameters for one test iteration. The test runs once per row. @DataProvider(name="loginData") public Object[][] data() { return new Object[][] {{"admin","pass123",true}, {"invalid","wrong",false}}; }. Used with: @Test(dataProvider="loginData") public void testLogin(String user, String pass, boolean expected). @Parameters reads values from testng.xml: <parameter name="browser" value="chrome"/>. Received with @Parameters({"browser"}) before the method. Key difference: DataProvider is for test-level data with multiple iterations (login credentials, search terms). @Parameters is for environment-level configuration (browser name, base URL, environment). I use DataProvider with ExcelReader for large datasets — the DataProvider method reads Excel and returns the data as Object[][].
Q: Hard Assert vs Soft Assert — when do you use each?
A: Hard Assert (Assert.assertEquals) stops test execution immediately on the first failure. If the login assertion fails, nothing after it runs. Soft Assert (SoftAssert) collects all failures and continues. You MUST call softAssert.assertAll() at the end to report them. Use Hard Assert for preconditions — if login fails, checking the dashboard is pointless. Use Soft Assert when verifying multiple independent things on one page — title, header text, footer links, navigation items — where you want to see ALL failures in one run instead of fixing them one by one, re-running, and finding the next failure. The most common mistake: forgetting assertAll(). Without it, all failures are silently swallowed and the test passes when it should fail.
Q: How do you achieve parallel execution in TestNG?
A: In testng.xml: <suite parallel="methods" thread-count="4">. Four modes: methods (each test method in its own thread), classes (each class in its own thread), tests (each <test> tag in its own thread), instances (each class instance in its own thread). The critical requirement: each thread must have its own WebDriver. I use ThreadLocal<WebDriver> in BaseTest: private static ThreadLocal<WebDriver> driver = new ThreadLocal<>();. In @BeforeMethod: driver.set(new ChromeDriver()). The getDriver() method returns driver.get(). Without ThreadLocal, all threads share one browser and tests fail randomly — elements being clicked by the wrong thread. I use parallel="methods" with thread-count="4" for speed, and verify thread safety by running the suite multiple times.
Q: What are TestNG Listeners? Which ones have you used?
A: Listeners hook into TestNG lifecycle events. I have used: (1) ITestListener — has onTestStart, onTestSuccess, onTestFailure, onTestSkipped. I use onTestFailure to capture screenshots and log failure details to Allure. (2) IRetryAnalyzer — retry(ITestResult result) returns true to retry. I set MAX_RETRY = 2 for handling flaky tests in CI. (3) IAnnotationTransformer — dynamically modifies @Test annotation at runtime. I use transform() to apply RetryAnalyzer to ALL tests globally without annotating each one. (4) ISuiteListener — onStart and onFinish for the entire suite. I use onFinish to send email with test results. Register listeners in testng.xml: <listeners><listener class-name="com.project.listeners.TestListener"/></listeners>. Or use @Listeners annotation on the test class.
Q: What is IRetryAnalyzer? How do you implement and apply it globally?
A: IRetryAnalyzer automatically retries failed tests. Implementation: public class RetryAnalyzer implements IRetryAnalyzer { private int count = 0; private static final int MAX = 2; public boolean retry(ITestResult result) { if (count < MAX) { count++; return true; } return false; } }. Apply to one test: @Test(retryAnalyzer = RetryAnalyzer.class). Apply globally using IAnnotationTransformer: public class AnnotationTransformer implements IAnnotationTransformer { public void transform(ITestAnnotation annotation, ...) { annotation.setRetryAnalyzerClass(RetryAnalyzer.class); } }. Register in testng.xml. This way every @Test automatically gets retry behavior. Important: retries are a safety net, not a fix. I track which tests need retries — if a test consistently retries, I investigate the root cause instead of relying on retry as a permanent solution.
Q: What are groups? How do you organize test execution?
A: Groups categorize tests for selective execution. Assign with @Test(groups = {"smoke", "regression"}). In testng.xml: <groups><run><include name="smoke"/></run></groups>. My organization: "smoke" — 15-20 critical path tests (login, core flows) run on every build. "regression" — full 200+ test suite run nightly. "sanity" — quick checks run after deployment. From Maven: mvn test -Dgroups=smoke. I maintain separate testng.xml files: testng-smoke.xml, testng-regression.xml. Group dependencies: <dependencies><group name="regression" depends-on="smoke"/></dependencies>. This gives flexibility — developers run smoke before pushing, CI runs regression nightly, and we run sanity after each deployment.
Q: What is testng.xml and why is it important?
A: testng.xml is the suite configuration file — the single source of truth for how tests run. It controls: which classes and methods to include/exclude, groups to run, parallel mode and thread count, parameters (browser, URL), listener registrations, and test dependencies. I have multiple testng.xml files: testng-smoke.xml (critical path), testng-regression.xml (full suite), testng-crossbrowser.xml (Chrome + Firefox + Edge in parallel). Maven Surefire plugin points to the file: mvn test -DsuiteXmlFile=testng-smoke.xml. This separates execution configuration from test code — I can change what runs, how it runs, and where it runs without touching a single test class.
Q: dependsOnMethods vs priority — what is the difference?
A: priority controls execution order — lower number runs first. Default is 0. @Test(priority=1) runs after @Test(priority=0). But if a lower-priority test fails, higher-priority tests still run. dependsOnMethods creates a hard dependency — if the dependency fails, the dependent test is SKIPPED, not failed. @Test(dependsOnMethods={"testLogin"}) testDashboard — if testLogin fails, testDashboard is marked SKIPPED. Use priority for suggested ordering. Use dependsOnMethods when running a test is genuinely pointless without the dependency passing. My rule: I avoid dependsOnMethods as much as possible. Tests should be independent — each one sets up its own state. Dependent tests cannot run in parallel and are harder to debug. If I must chain, I limit it to 2-3 tests maximum.
Q: How do you disable a test and how do you re-run only failed tests?
A: Disable: @Test(enabled = false) — the test is skipped but stays in code. I always add a comment: // TODO: Re-enable after JIRA-1234 is fixed. I also flag disabled tests in code reviews — if a test has been disabled for more than a sprint, it should be fixed or deleted. Exclude in testng.xml: <methods><exclude name="testBrokenFeature"/></methods>. Re-run failed tests: after a run, TestNG generates test-output/testng-failed.xml containing only failed tests. Run it: mvn test -DsuiteXmlFile=test-output/testng-failed.xml. Combined with IRetryAnalyzer, my pipeline auto-retries failures 2 times, then generates the failed XML for manual investigation. Jenkins TestNG Plugin shows which tests failed and allows one-click re-run.
Key Point: TestNG questions are about how you configured your framework — testng.xml, listeners, retry logic, and parallel execution setup.