Selenium Cucumber BDD framework with ChatGPT

Dr. Çağrı ATASEVEN
12 min readMar 7, 2023

Science fiction is a common belief; One day, robots will replace humans. Before I met ChatGPT, I didn’t think it was highly likely, but now I’m not so sure. I’m sure there is no one left who hasn’t heard from or even tried ChatGPT anymore. Isn’t it really unbelievable?

Now I will share with you about 20 minutes of conversation I had with ChatGPT. So what did I achieve as a result of this conversation? The answer is the Cucumber BDD framework with Selenium.

Warm-Up Laps

Selenium Demo with Python and Java

from selenium import webdriver
from selenium.webdriver.common.keys import Keys

# open Chrome browser using Selenium WebDriver
driver = webdriver.Chrome()

# navigate to Google
driver.get("https://www.google.com")

# find the search box and enter a keyword
search_box = driver.find_element_by_name("q")
search_box.send_keys("selenium")

# submit the search query
search_box.submit()

# wait for the results to load
driver.implicitly_wait(10)

# print the search results to the console
results = driver.find_elements_by_css_selector("div.g")
for result in results:
print(result.text)

# close the browser
driver.quit()
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;

public class SeleniumDemo {
public static void main(String[] args) {
// set the path of the ChromeDriver executable
System.setProperty("webdriver.chrome.driver", "path/to/chromedriver.exe");

// create a new instance of the ChromeDriver
WebDriver driver = new ChromeDriver();

// navigate to Google
driver.get("https://www.google.com");

// find the search box and enter a keyword
WebElement searchBox = driver.findElement(By.name("q"));
searchBox.sendKeys("selenium");

// submit the search query
searchBox.submit();

// wait for the results to load
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

// print the search results to the console
List<WebElement> results = driver.findElements(By.cssSelector("div.g"));
for (WebElement result : results) {
System.out.println(result.getText());
}

// close the browser
driver.quit();
}
}

Creating Login Page

<!DOCTYPE html>
<html>
<head>
<title>Login Page</title>
</head>
<body>
<h1>Login Page</h1>
<form>
<label for="username">Username:</label>
<input type="text" id="username" name="username"><br><br>
<label for="password">Password:</label>
<input type="password" id="password" name="password"><br><br>
<input type="submit" value="Login">
</form>
</body>
</html>
from selenium import webdriver
from selenium.webdriver.common.keys import Keys

# open Chrome browser using Selenium WebDriver
driver = webdriver.Chrome()

# navigate to the login page
driver.get("file:///path/to/loginpage.html")

# find the username input box and enter a username
username_box = driver.find_element_by_id("username")
username_box.send_keys("myusername")

# find the password input box and enter a password
password_box = driver.find_element_by_id("password")
password_box.send_keys("mypassword")

# click the login button
login_button = driver.find_element_by_css_selector("input[type='submit']")
login_button.click()

# close the browser
driver.quit()

Creating Page Object Model Design pattern

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;

public class LoginPage {
private WebDriver driver;

@FindBy(id = "username")
private WebElement usernameInput;

@FindBy(id = "password")
private WebElement passwordInput;

@FindBy(css = "input[type='submit']")
private WebElement loginButton;

public LoginPage(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}

public void enterUsername(String username) {
usernameInput.sendKeys(username);
}

public void enterPassword(String password) {
passwordInput.sendKeys(password);
}

public void clickLoginButton() {
loginButton.click();
}
}
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

public class LoginTest {
public static void main(String[] args) {
// set the path of the ChromeDriver executable
System.setProperty("webdriver.chrome.driver", "path/to/chromedriver.exe");

// create a new instance of the ChromeDriver
WebDriver driver = new ChromeDriver();

// navigate to the login page
driver.get("file:///path/to/loginpage.html");

// create a new instance of the LoginPage class
LoginPage loginPage = new LoginPage(driver);

// enter a username and password, and click the login button
loginPage.enterUsername("myusername");
loginPage.enterPassword("mypassword");
loginPage.clickLoginButton();

// close the browser
driver.quit();
}
}

Cucumber BDD tests

Feature: Login Functionality
As a user
I want to be able to login to the application
So that I can access my account

Scenario: Valid Login
Given I am on the login page
When I enter valid credentials
And click the login button
Then I should be redirected to the home page

Scenario: Invalid Login
Given I am on the login page
When I enter invalid credentials
And click the login button
Then I should see an error message
import org.junit.Assert;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;

public class LoginStepDefinitions {
private WebDriver driver;
private LoginPage loginPage;
private HomePage homePage;

@Given("I am on the login page")
public void i_am_on_the_login_page() {
System.setProperty("webdriver.chrome.driver", "path/to/chromedriver.exe");
driver = new ChromeDriver();
loginPage = new LoginPage(driver);
driver.get("file:///path/to/loginpage.html");
}

@When("I enter valid credentials")
public void i_enter_valid_credentials() {
loginPage.enterUsername("valid_username");
loginPage.enterPassword("valid_password");
loginPage.clickLoginButton();
}

@When("I enter invalid credentials")
public void i_enter_invalid_credentials() {
loginPage.enterUsername("invalid_username");
loginPage.enterPassword("invalid_password");
loginPage.clickLoginButton();
}

@Then("I should be redirected to the home page")
public void i_should_be_redirected_to_the_home_page() {
homePage = new HomePage(driver);
Assert.assertEquals("Home Page Title", homePage.getTitle());
}

@Then("I should see an error message")
public void i_should_see_an_error_message() {
Assert.assertTrue(loginPage.isErrorMessageDisplayed());
}

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

Scenario Outline and Examples

Feature: Login Functionality
As a user
I want to be able to login to the application
So that I can access my account

Scenario Outline: Login with valid and invalid credentials
Given I am on the login page
When I enter "<username>" and "<password>"
And click the login button
Then I should see "<expected_result>"

Examples:
| username | password | expected_result |
| valid_user | valid_pass | redirected to homepage |
| invalid_user | invalid_pass | error message displayed |
import org.junit.Assert;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import io.cucumber.datatable.DataTable;

public class LoginStepDefinitions {
private WebDriver driver;
private LoginPage loginPage;
private HomePage homePage;

@Given("I am on the login page")
public void i_am_on_the_login_page() {
System.setProperty("webdriver.chrome.driver", "path/to/chromedriver.exe");
driver = new ChromeDriver();
loginPage = new LoginPage(driver);
driver.get("file:///path/to/loginpage.html");
}

@When("I enter {string} and {string}")
public void i_enter_and(String username, String password) {
loginPage.enterUsername(username);
loginPage.enterPassword(password);
loginPage.clickLoginButton();
}

@Then("I should see {string}")
public void i_should_see(String expected_result) {
if (expected_result.equals("redirected to homepage")) {
homePage = new HomePage(driver);
Assert.assertEquals("Home Page Title", homePage.getTitle());
} else if (expected_result.equals("error message displayed")) {
Assert.assertTrue(loginPage.isErrorMessageDisplayed());
}
}

@When("I enter the following credentials")
public void i_enter_the_following_credentials(DataTable dataTable) {
List<Map<String, String>> rows = dataTable.asMaps(String.class, String.class);
for (Map<String, String> row : rows) {
String username = row.get("username");
String password = row.get("password");
String expected_result = row.get("expected_result");
i_enter_and(username, password);
i_should_see(expected_result);
}
}

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

Runner Class

import org.junit.runner.RunWith;
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;

@RunWith(Cucumber.class)
@CucumberOptions(features = "classpath:features",
glue = {"stepdefinitions"},
plugin = {"pretty", "html:target/cucumber-reports"})
public class TestRunner {
}

DriverFactory and Hooks Class

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.remote.DesiredCapabilities;

public class DriverFactory {
public static WebDriver createDriver(String browser) {
WebDriver driver = null;
switch (browser.toLowerCase()) {
case "chrome":
System.setProperty("webdriver.chrome.driver", "path/to/chromedriver.exe");
driver = new ChromeDriver();
break;
case "firefox":
System.setProperty("webdriver.gecko.driver", "path/to/geckodriver.exe");
driver = new FirefoxDriver();
break;
case "ie":
DesiredCapabilities capabilities = DesiredCapabilities.internetExplorer();
capabilities.setCapability(InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS, true);
System.setProperty("webdriver.ie.driver", "path/to/IEDriverServer.exe");
driver = new InternetExplorerDriver(capabilities);
break;
case "edge":
System.setProperty("webdriver.edge.driver", "path/to/msedgedriver.exe");
driver = new EdgeDriver();
break;
default:
throw new IllegalArgumentException("Unsupported browser: " + browser);
}
return driver;
}
}
import org.junit.Assert;
import org.openqa.selenium.WebDriver;

import io.cucumber.java.After;
import io.cucumber.java.Before;
import io.cucumber.java.Scenario;

public class Hooks {
private WebDriver driver;

@Before
public void setUp(Scenario scenario) {
String browser = System.getProperty("browser", "chrome");
driver = DriverFactory.createDriver(browser);
}

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

A simple recommendation: Singleton Design

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.remote.DesiredCapabilities;

public class DriverFactory {
private static WebDriver driver;

private DriverFactory() {
// Prevent instantiation
}

public static synchronized WebDriver getDriver(String browser) {
if (driver == null) {
switch (browser.toLowerCase()) {
case "chrome":
System.setProperty("webdriver.chrome.driver", "path/to/chromedriver.exe");
driver = new ChromeDriver();
break;
case "firefox":
System.setProperty("webdriver.gecko.driver", "path/to/geckodriver.exe");
driver = new FirefoxDriver();
break;
case "ie":
DesiredCapabilities capabilities = DesiredCapabilities.internetExplorer();
capabilities.setCapability(InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS, true);
System.setProperty("webdriver.ie.driver", "path/to/IEDriverServer.exe");
driver = new InternetExplorerDriver(capabilities);
break;
case "edge":
System.setProperty("webdriver.edge.driver", "path/to/msedgedriver.exe");
driver = new EdgeDriver();
break;
default:
throw new IllegalArgumentException("Unsupported browser: " + browser);
}
}
return driver;
}

public static synchronized void quitDriver() {
if (driver != null) {
driver.quit();
driver = null;
}
}
}
import io.cucumber.java.After;
import io.cucumber.java.Before;
import io.cucumber.java.Scenario;
import org.openqa.selenium.WebDriver;

public class Hooks {
private WebDriver driver;

@Before
public void setUp(Scenario scenario) {
String browser = System.getProperty("browser", "chrome");
driver = DriverFactory.getDriver(browser);
}

@After
public void tearDown() {
DriverFactory.quitDriver();
}
}

Full-Screen Config

import org.openqa.selenium.Dimension;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.remote.DesiredCapabilities;

public class DriverFactory {
private static WebDriver driver;

private DriverFactory() {
// Prevent instantiation
}

public static synchronized WebDriver getDriver(String browser) {
if (driver == null) {
switch (browser.toLowerCase()) {
case "chrome":
System.setProperty("webdriver.chrome.driver", "path/to/chromedriver.exe");
driver = new ChromeDriver();
break;
case "firefox":
System.setProperty("webdriver.gecko.driver", "path/to/geckodriver.exe");
driver = new FirefoxDriver();
break;
case "ie":
DesiredCapabilities capabilities = DesiredCapabilities.internetExplorer();
capabilities.setCapability(InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS, true);
System.setProperty("webdriver.ie.driver", "path/to/IEDriverServer.exe");
driver = new InternetExplorerDriver(capabilities);
break;
case "edge":
System.setProperty("webdriver.edge.driver", "path/to/msedgedriver.exe");
driver = new EdgeDriver();
break;
default:
throw new IllegalArgumentException("Unsupported browser: " + browser);
}
driver.manage().window().maximize();
}
return driver;
}

public static synchronized void quitDriver() {
if (driver != null) {
driver.quit();
driver = null;
}
}
}
import io.cucumber.java.After;
import io.cucumber.java.Before;
import org.openqa.selenium.WebDriver;

public class Hooks {
private static WebDriver driver;

@Before
public void setUp() {
String browser = System.getProperty("browser", "chrome");
driver = DriverFactory.getDriver(browser);
driver.manage().window().maximize();
}

@After
public void tearDown() {
DriverFactory.quitDriver();
}

public static WebDriver getDriver() {
return driver;
}
}

SSL Handling

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.edge.EdgeOptions;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;

public class DriverFactory {
private static WebDriver driver;

public static WebDriver getDriver(String browser) {
if (driver == null) {
driver = createDriver(browser);
}
return driver;
}

public static void quitDriver() {
if (driver != null) {
driver.quit();
driver = null;
}
}

private static WebDriver createDriver(String browser) {
WebDriver driver = null;
switch (browser) {
case "firefox":
FirefoxOptions firefoxOptions = new FirefoxOptions();
firefoxOptions.setAcceptInsecureCerts(true);
driver = new FirefoxDriver(firefoxOptions);
break;
case "edge":
EdgeOptions edgeOptions = new EdgeOptions();
edgeOptions.setAcceptInsecureCerts(true);
driver = new EdgeDriver(edgeOptions);
break;
default:
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.setAcceptInsecureCerts(true);
driver = new ChromeDriver(chromeOptions);
break;
}
return driver;
}
}

config.properties

login_page_url=https://www.example.com/login
home_page_url=https://www.example.com/home
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

public class ConfigReader {
private static Properties properties;

static {
try {
FileInputStream fileInputStream = new FileInputStream("config.properties");
properties = new Properties();
properties.load(fileInputStream);
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}

public static String getLoginPageUrl() {
return properties.getProperty("login_page_url");
}

public static String getHomePageUrl() {
return properties.getProperty("home_page_url");
}
}
public class LoginPageTests {
private WebDriver driver;
private String loginPageUrl;

@Before
public void setUp() {
driver = DriverFactory.getDriver("chrome");
loginPageUrl = ConfigReader.getLoginPageUrl();
driver.get(loginPageUrl);
// ...
}

// ...
}

Cucumber Report

<dependency>
<groupId>com.aventstack</groupId>
<artifactId>extentreports</artifactId>
<version>5.0.8</version>
</dependency>
import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.reporter.ExtentHtmlReporter;

public class BaseTest {
protected static ExtentReports extentReports;

@BeforeClass
public static void setUp() {
extentReports = new ExtentReports();
ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter("test-output/extent-report.html");
extentReports.attachReporter(htmlReporter);

extentReports.setSystemInfo("OS", System.getProperty("os.name"));
extentReports.setSystemInfo("Java Version", System.getProperty("java.version"));
// ...
}

@AfterClass
public static void tearDown() {
extentReports.flush();
}
}
import org.junit.runner.RunWith;

import cucumber.api.CucumberOptions;
import cucumber.api.junit.Cucumber;

@RunWith(Cucumber.class)
@CucumberOptions(
// ...
)
public class RunCucumberTests extends BaseTest {

}
import com.aventstack.extentreports.ExtentTest;
import com.aventstack.extentreports.Status;
import com.aventstack.extentreports.service.ExtentTestManager;

import cucumber.api.Result;
import cucumber.api.event.EventPublisher;
import cucumber.api.event.TestStepFinished;

public class CucumberReportListener implements EventListener {
private ExtentTest extentTest;

@Override
public void setEventPublisher(EventPublisher publisher) {
publisher.registerHandlerFor(TestStepFinished.class, testStepFinished -> {
Result result = testStepFinished.getResult();
if (result.getStatus() == Result.SUCCESS) {
extentTest.log(Status.PASS, "Step passed: " + testStepFinished.getTestStep().getCodeLocation());
} else if (result.getStatus() == Result.FAILURE) {
extentTest.log(Status.FAIL, "Step failed: " + testStepFinished.getTestStep().getCodeLocation());
extentTest.fail(result.getError());
} else if (result.getStatus() == Result.SKIPPED) {
extentTest.log(Status.SKIP, "Step skipped: " + testStepFinished.getTestStep().getCodeLocation());
}
});
}

@BeforeTestStep
public void beforeTestStepStart(TestStepStarted testStepStarted) {
extentTest = ExtentTestManager.createTest(testStepStarted.getTestStep().toString());
}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>selenium-cucumber-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>Selenium Cucumber Demo</name>
<url>http://maven.apache.org</url>

<properties>
<java.version>1.8</java.version>
<cucumber.version>5.7.0</cucumber.version>
<junit.version>4.12</junit.version>
<selenium.version>3.141.59</selenium.version>
</properties>

<dependencies>
<!-- Selenium -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
</dependency>

<!-- Cucumber -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>${cucumber.version}</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit</artifactId>
<version>${cucumber.version}</version>
</dependency>

<!-- JUnit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>

<!-- Cucumber Reporting -->
<dependency>
<groupId>net.masterthought</groupId>
<artifactId>cucumber-reporting</artifactId>
<version>5.6.0</version>
</dependency>
</dependencies>

<build>
<plugins>
<!-- Set Java Compiler Version -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>

<!-- Cucumber Reporting Plugin -->
<plugin>
<groupId>net.masterthought</groupId>
<artifactId>maven-cucumber-reporting</artifactId>
<version>5.6.0</version>
<executions>
<execution>
<id>generate-report</id>
<phase>verify</phase>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<projectName>Selenium Cucumber Demo</projectName>
<outputDirectory>${project.build.directory}/cucumber-report-html</outputDirectory>
<cucumberOutput>${project.build.directory}/cucumber.json</cucumberOutput>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

README.md

# Selenium Automation Framework

This is a Selenium-based automation framework designed for testing the login page of a web application. The framework is built using Java, Cucumber BDD, and the Page Object Model design pattern.

## Installation

1. Clone the repository to your local machine.
2. Install Java 8 or higher.
3. Install Maven.
4. Install an IDE such as Eclipse or IntelliJ.
5. Import the project into your IDE.
6. Install the necessary dependencies using Maven.

## Usage

To run the tests, simply run the `RunCucumberTest` class. The test results will be generated in the `target/cucumber-reports` directory.

## Configuration

The framework supports the following browsers:

- Chrome
- Firefox
- Edge
- Internet Explorer

The default browser is Chrome, but you can change it by modifying the `browser` parameter in the `config.properties` file.

You can also set the URL of the web application under test in the `config.properties` file.

## Reporting

The framework generates a cucumber-html-report that shows the results of each scenario, including the steps that were executed, the status of each step, and any associated screenshots.

## Contributors

- John Doe (johndoe@example.com)

## License

This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details.

Conclusion

I guess there is no limit to what ChatGPT can do to improve the framework. But I didn’t want to prolong the article that much :) It is certain that our professional lives and even our private lives will change at an unbelievable speed in the near future. And all that matters is being able to ask the right question.

--

--

Dr. Çağrı ATASEVEN

Cagri Ataseven is ISTQB Certified Software Test Automation Engineer, Also, he has a Ph.D. in mathematics.