Pages

Page Object Model with Cucumber, Selenium, and TestNG

  


To implement Page Object Model (POM) in CucumberSelenium, and TestNG, you structure your automation framework to separate page-related codetest step definitions, and test runners for modularity and maintainability.


What is Page Object Model (POM)?

POM is a design pattern in Selenium that creates an object repository for web UI elements. It helps reduce code duplication and improves test maintenance.


Page Object Model Implementation


pom.xml:

<dependencies>
    <dependency>
        <groupId>io.cucumber</groupId>
        <artifactId>cucumber-java</artifactId>
        <version>7.14.0</version>
    </dependency>
    <dependency>
        <groupId>io.cucumber</groupId>
        <artifactId>cucumber-testng</artifactId>
        <version>7.14.0</version>
    </dependency>
    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-java</artifactId>
        <version>4.21.0</version>
    </dependency>
    <dependency>
        <groupId>org.testng</groupId>
        <artifactId>testng</artifactId>
        <version>7.10.0</version>
        <scope>test</scope>
    </dependency>
</dependencies>




LoginPage.java (Page Object Class)

package pages;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;

public class LoginPage {
    WebDriver driver;

    // Constructor
    public LoginPage(WebDriver driver) {
        this.driver = driver;
    }

    // Locators
    By username = By.id("username");
    By password = By.id("password");
    By loginBtn = By.id("login");

    // Actions
    public void enterUsername(String user) {
        driver.findElement(username).sendKeys(user);
    }

    public void enterPassword(String pass) {
        driver.findElement(password).sendKeys(pass);
    }

    public void clickLogin() {
        driver.findElement(loginBtn).click();
    }
}





BaseTest.java – Setup WebDriver

package utils;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import io.cucumber.java.After;
import io.cucumber.java.Before;

public class BaseTest {
    public static WebDriver driver;

    @Before
    public void setUp() {
        driver = new ChromeDriver();
        driver.manage().window().maximize();
    }

    @After
    public void tearDown() {
        driver.quit();
    }
}




LoginSteps.java – Step Definitions

package stepdefinitions;

import io.cucumber.java.en.*;
import pages.LoginPage;
import utils.BaseTest;

public class LoginSteps {
    LoginPage loginPage;

    @Given("user is on login page")
    public void user_is_on_login_page() {
        BaseTest.driver.get("https://example.com/login");
        loginPage = new LoginPage(BaseTest.driver);
    }

    @When("user enters username {string} and password {string}")
    public void user_enters_credentials(String username, String password) {
        loginPage.enterUsername(username);
        loginPage.enterPassword(password);
    }

    @And("clicks on login button")
    public void clicks_login_button() {
        loginPage.clickLogin();
    }

    @Then("user should be redirected to dashboard")
    public void user_redirected_to_dashboard() {
        String url = BaseTest.driver.getCurrentUrl();
        assert url.contains("dashboard");
    }
}




login.feature – Feature File

Feature: Login Feature

  Scenario: Successful login
    Given user is on login page
    When user enters username "admin" and password "admin123"
    And clicks on login button
    Then user should be redirected to dashboard




TestRunner.java – Cucumber Test Runner using TestNG

package testrunner;

import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;

@CucumberOptions(
    features = "src/test/java/features",
    glue = {"stepdefinitions", "utils"},
    plugin = {"pretty", "html:target/cucumber-report.html"},
    monochrome = true
)
public class TestRunner extends AbstractTestNGCucumberTests {
}


How it works together

  • login.feature defines the behavior.

  • LoginSteps.java maps Gherkin steps to Java code.

  • LoginPage.java abstracts UI elements & actions.

  • TestRunner.java executes scenarios using TestNG.

  • BaseTest.java manages driver lifecycle.



Benefits of POM + Cucumber + Selenium + TestNG

FeatureBenefit
POMEasy maintainability & reuse of locators
CucumberReadable tests in Gherkin format
SeleniumBrowser automation
TestNGTest management, reporting, parallel execution



Suggested Posts:

1. TestNG Reporter Log
2. Priority in TestNG
3. Test Runner in TestNG
4. Asserts in TestNG
5. Invocation Count in TestNG

How to use TestNG with Selenium

  

Below are the steps and code to integrate TestNG with Selenium in Java.


What is TestNG?

TestNG (Test Next Generation) is a testing framework inspired by JUnit and NUnit. It is designed to make testing easier and more powerful with:

  • Annotations (@Test, @BeforeMethod, @AfterMethod, etc.)

  • Grouping test cases

  • Parallel execution

  • XML-based configuration

  • Data-driven testing


Steps to Integrate TestNG with Selenium Java

  • Install TestNG Plugin in Eclipse

    • Go to Help > Eclipse Marketplace

    • Search for TestNG, install, and restart Eclipse

  • Add Selenium and TestNG Dependencies in pom.xml

    <dependencies>
        <!-- Selenium -->
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>4.21.0</version>
        </dependency>
    
        <!-- TestNG -->
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>7.9.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    

  1. Write TestNG Test Class with Selenium Code


Example Code

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

public class GoogleSearchTest {

    WebDriver driver;

    @BeforeClass
    public void setup() {
        // Set path to chromedriver
        System.setProperty("webdriver.chrome.driver", "path/to/chromedriver");
        driver = new ChromeDriver();
        driver.manage().window().maximize();
    }

    @Test
    public void testGoogleSearch() {
        driver.get("https://www.google.com");

        WebElement searchBox = driver.findElement(By.name("q"));
        searchBox.sendKeys("Selenium WebDriver");
        searchBox.submit();

        // Wait and verify title
        try { Thread.sleep(2000); } catch (InterruptedException e) {}
        String title = driver.getTitle();
        Assert.assertTrue(title.contains("Selenium WebDriver"), "Title does not contain expected text!");
    }

    @AfterClass
    public void tearDown() {
        if (driver != null) {
            driver.quit();
        }
    }
}


How to Run TestNG Test
  • Right-click on the test file →  Run As → TestNG Test
  • Or create a testng.xml:
    <!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd" >
    <suite name="MyTestSuite">
        <test name="GoogleTest">
            <classes>
                <class name="GoogleSearchTest" />
            </classes>
        </test>
    </suite>
    
    Then right-click on testng.xml → Run As → TestNG Suite.



    TestNG Annotations Summary


    AnnotationDescription
    @TestMarks a test method
    @BeforeClassRuns once before any method in the class
    @AfterClassRuns once after all methods in the class
    @BeforeMethodRuns before each test method
    @AfterMethodRuns after each test method



    Suggested Posts:

    1. TestNG Reporter Log
    2. Priority in TestNG
    3. Test Runner in TestNG
    4. Asserts in TestNG
    5. TestNG Reports

    Test Runner in Cucumber using TestNG

      


    What is a Test Runner Class in Cucumber?

    Test Runner class in Cucumber is used to execute feature files and link them with step definition code. It acts as a bridge between:

    • Feature files (written in Gherkin syntax) and

    • Step Definition classes (Java methods mapped to steps).


    In the context of Cucumber with TestNG, the Test Runner allows us to run scenarios just like regular TestNG test cases, and we get benefits like:

    • Parallel execution,

    • TestNG XML suite configuration,

    • Integration with TestNG annotations (@BeforeClass@AfterClass etc.).


    Key Responsibilities of a Test Runner Class

    1. Specify the location of feature files

    2. Specify the location of step definitions

    3. Configure other Cucumber options like plugins, tags, dryRun, etc.

    4. Use @CucumberOptions to customize execution

    5. Extend AbstractTestNGCucumberTests when using TestNG



    Implementing Test Runner Class Using TestNG

    Folder Structure Example:

    project-root/
    ├── src/test/java/
    │   ├── features/
    │   │   └── login.feature
    │   ├── stepDefinitions/
    │   │   └── LoginSteps.java
    │   └── runner/
    │       └── TestRunner.java



    Below are steps to Implement Test Runner in Cucumber using Testng 


    1. Add Maven dependencies in pom.xml:

    <dependencies>
        <!-- Cucumber dependencies -->
        <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>
    
        <!-- TestNG dependency -->
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>7.10.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    



    2. Write the Feature File (login.feature)

    Feature: Login Functionality
    
      Scenario: Successful login with valid credentials
        Given user is on login page
        When user enters valid username and password
        Then user should be redirected to the homepage
    



    3. Write Step Definitions (LoginSteps.java)

    package stepDefinitions;
    
    import io.cucumber.java.en.*;
    
    public class LoginSteps {
        
        @Given("user is on login page")
        public void user_is_on_login_page() {
            System.out.println("User is on login page");
        }
    
        @When("user enters valid username and password")
        public void user_enters_valid_credentials() {
            System.out.println("User entered username and password");
        }
    
        @Then("user should be redirected to the homepage")
        public void user_should_see_homepage() {
            System.out.println("User is on homepage");
        }
    }
    




    4. Create Test Runner Class (TestRunner.java)

    package runner;
    
    import io.cucumber.testng.AbstractTestNGCucumberTests;
    import io.cucumber.testng.CucumberOptions;
    
    @CucumberOptions(
        features = "src/test/java/features",
        glue = "stepDefinitions",
        plugin = {
            "pretty",
            "html:target/cucumber-reports.html",
            "json:target/cucumber.json"
        },
        monochrome = true
    )
    public class TestRunner extends AbstractTestNGCucumberTests {
    }
    


    How to Run It?

    Option 1: Run TestRunner.java directly from your IDE (Eclipse/IntelliJ)

    Option 2: Add TestNG XML to run from command line or CI

    <suite name="Cucumber Test Suite">
      <test name="Cucumber Scenarios">
        <classes>
          <class name="runner.TestRunner" />
        </classes>
      </test>
    </suite>
    



    Advantages of Using TestNG with Cucumber:

    • Easily manage test suites using testng.xml

    • Group and prioritize tests

    • Run parallel tests using TestNG support

    • Better integration with reporting tools and Jenkins



    Important Points:

    PropertyDescription
    featuresPath to .feature files
    gluePackage for step definitions and hooks
    pluginReporting plugins (e.g., HTML, JSON, pretty)
    monochromeDisplay readable console output
    dryRuntrue to check if step definitions exist for each step, without executing



    Suggested Posts:

    1. TestNG with Selenium
    2. Annotations in TestNG
    3. Test Runner in TestNG
    4. Asserts in TestNG
    5. TestNG Reports

    How to use @Factory Annotation in TestNG



    In TestNG, the @Factory annotation is used to create instances of test classes dynamically at runtime, particularly when you want to run the same test class multiple times with different sets of data or configurations .


    Purpose of @Factory:

    • Allows creation of multiple instances of a test class.

    • Enables running the same tests with different input values.

    • Useful for data-driven testing.


    Key Characteristics:

    • @Factory is applied on a method, not a class.

    • The factory method should return an array of Object, where each object is an instance of a test class.

    • Can be combined with constructors to pass parameters into the test class.


    Example: Using @Factory in TestNG

    Let's say you want to run the same test with different browser names:


    Step 1: Test Class

    import org.testng.annotations.Test;
    
    public class BrowserTest {
    
        private String browser;
    
        // Constructor to accept the browser name
        public BrowserTest(String browser) {
            this.browser = browser;
        }
    
        @Test
        public void openBrowserTest() {
            System.out.println("Running test on: " + browser);
        }
    }
    




    Step 2: Factory Class

    import org.testng.annotations.Factory;
    
    public class BrowserTestFactory {
    
        @Factory
        public Object[] createInstances() {
            return new Object[] {
                new BrowserTest("Chrome"),
                new BrowserTest("Firefox"),
                new BrowserTest("Edge")
            };
        }
    }
    



    Step 3: testng.xml

    <!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd" >
    <suite name="Browser Suite">
        <test name="Browser Test Factory">
            <classes>
                <class name="BrowserTestFactory" />
            </classes>
        </test>
    </suite>
    



    Output:

    Running test on: Chrome
    Running test on: Firefox
    Running test on: Edge
    


    When to Use @Factory:

    • You want to test the same functionality with different data.

    • You prefer passing parameters via constructors rather than using @DataProvider.

    • You need complex test class instantiation logic.




    Difference Between @Factory and @DataProvider:

    Feature@Factory@DataProvider
    UsageInstantiates test classesFeeds data to test methods
    Applied onMethodMethod
    ReturnsObject[] (instances)Object[][] (data sets)
    Constructor usageYesNo (generally, parameters in test methods)



    Suggested Posts:

    1. POM in TestNG
    2. Annotations in TestNG
    3. Expected Exceptions in TestNG
    4. Asserts in TestNG
    5. TestNG Reports