Answer
Migrating Legacy Selenium TestNG to Cucumber BDD
Why Not Big-Bang Migration?
A full rewrite is high-risk:
- ✓3-6 months with no test coverage
- ✓New bugs introduced during rewrite
- ✓Team unfamiliar with Cucumber + old code simultaneously
Incremental migration maintains coverage throughout.
Migration Phases
Phase 1: Setup (Week 1) — Add Cucumber Without Removing Anything
XML
<!-- Add Cucumber to existing pom.xml — keep TestNG -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>7.15.0</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-testng</artifactId>
<version>7.15.0</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-picocontainer</artifactId>
<version>7.15.0</version>
</dependency>
Keep all existing Page Objects — they are reusable as-is. Cucumber step definitions call Page Object methods exactly like TestNG tests did.
Phase 2: Reuse Existing Page Objects in Step Definitions
Java
// EXISTING Page Object (no changes needed)
public class LoginPage {
public LoginPage(WebDriver driver) { ... }
public void enterUsername(String u) { ... }
public void enterPassword(String p) { ... }
public void clickLogin() { ... }
}
// NEW Cucumber Step Definition — calls same Page Object
public class LoginSteps {
private final ScenarioContext ctx;
public LoginSteps(ScenarioContext ctx) { this.ctx = ctx; }
@Given("the user is on the login page")
public void navigateToLogin() {
ctx.loginPage = new LoginPage(ctx.driver);
ctx.driver.get(ConfigReader.getBaseUrl() + "/login");
}
@When("the user logs in with username {string} and password {string}")
public void login(String user, String pass) {
ctx.loginPage.enterUsername(user); // same method call
ctx.loginPage.enterPassword(pass);
ctx.loginPage.clickLogin();
}
}
Phase 3: Prioritize Which Tests to Convert First
CODE
Conversion priority:
1. Happy path / smoke tests (highest business value)
2. Tests that BAs/POs want to review (living documentation value)
3. Tests that are frequently run and change often
4. Complex data-driven tests (Scenario Outline replaces TestNG @DataProvider)
Defer migration:
5. Internal unit/integration tests (keep in TestNG)
6. Tests with complex setup that won''t benefit from Gherkin
Phase 4: Run Both Frameworks in Parallel
XML
<!-- testng.xml — run both during transition -->
<suite name="Migration Suite" parallel="tests" thread-count="2">
<test name="Legacy TestNG Tests">
<classes>
<class name="tests.legacy.LoginTest"/>
<class name="tests.legacy.CheckoutTest"/>
</classes>
</test>
<test name="New Cucumber BDD Tests">
<classes>
<class name="runners.CucumberSmokeRunner"/>
</classes>
</test>
</suite>
Phase 5: Decommission Old Tests
CODE
As Cucumber coverage for a feature reaches 100%:
1. Verify Cucumber tests cover all cases the old tests covered
2. Run both suites in parallel for 2 sprints — confirm same results
3. Delete old TestNG test class
4. Update testng.xml to remove deleted class
Converting TestNG @DataProvider to Scenario Outline
Java
// BEFORE: TestNG
@DataProvider
public Object[][] loginData() {
return new Object[][] {
{"admin@test.com", "Admin@123", "Dashboard"},
{"user@test.com", "User@456", "Home"}
};
}
@Test(dataProvider = "loginData")
public void testLogin(String email, String pass, String page) { ... }
// AFTER: Cucumber
Scenario Outline: Login with <role>
When the user logs in as "<email>" with "<password>"
Then they should land on the "<page>" page
Examples:
| email | password | page |
| admin@test.com | Admin@123 | Dashboard |
| user@test.com | User@456 | Home |
Conversion Timeline (50 Test Classes)
CODE
Week 1: Framework setup + first 5 feature files (smoke)
Week 2-3: High-priority tests (20 feature files)
Week 4-5: Remaining tests (25 feature files)
Week 6: Remove old TestNG tests, final validation
