Who Is This Course For?
This course is for Selenium with Java engineers who want to implement BDD in their automation framework. You will build a complete BDD framework using Cucumber, JUnit, PicoContainer Dependency Injection, Page Object Model, and ExtentReports — the way real Agile teams actually work.
- What BDD (Behavior Driven Development) is — a process where Customer, BA, Dev, and QA collaborate using a shared language
- Three phases of BDD: Discovery (requirement discussion), Formulation (documentation), Automation (software/test implementation)
- The core problem BDD solves — "secure" means authentication to a dev, "sign-in" to QA, "access control" to a customer
Given— precondition;When— action performed;Then— expected outcomeAndkeyword — chaining multipleGiven/When/Thensteps- Gherkin is framework-agnostic — same syntax works in Cucumber (Java), Behave (Python), SpecFlow (.NET)
- Writing a complete Login scenario using
Given/When/Then/And
- Eclipse Maven project creation —
src/test/javafor code,src/test/resourcesfor.featurefiles - Installing Cucumber Eclipse Plugin from Eclipse Marketplace — enables syntax highlighting for
.featurefiles - Natural plugin (optional) — Gherkin auto-completion while editing feature files
cucumber-java— write step definitions in Java using@Given,@When,@Thencucumber-junit— JUnit-based test runner;@RunWith(Cucumber.class)selenium-java— WebDriver API for browser automationwebdrivermanager— auto-manages browser driver binaries; no manual path setupcucumber-picocontainer— enables constructor-based Dependency Injection across step classesextentreports— generates rich HTML reports with charts and screenshots
- File extension:
.feature— create undersrc/test/resources/features Featurekeyword + description — explains what this file covers- Comments using
#— different from Java's// Scenariokeyword — one test case; name it clearly and uniquely- Duplicate scenario names are allowed in feature files; duplicate step definition annotations are not
Background— a set of steps that run before every scenario in the same.featurefile- Limitation:
Backgroundonly supports setup (before), not teardown (after); scoped to one file only - Background with
Scenario Outline— runs once per iteration, not once total BackgroundvsHooks: Background is visible in the file; Hooks run silently in the background
- Annotations
@Given,@When,@Then— the text must exactly match the Gherkin sentence (case-sensitive, space-sensitive) - Unique annotation rule — two methods with the same text throw
AmbiguousStepDefinitionsException - Step definitions are package-scoped, not class-scoped —
gluesearches all classes in the package snippets = SnippetType.CAMELCASE— generates camelCase Java stubs matching Gherkin steps
- Modern Cucumber Expressions:
{string},{int},{float},{double},{word},{long} - Anonymous parameter
{}— accepts any value when the data type is not known in advance - Legacy Regular Expressions —
^(beginning),$(end), capture groups(.*)for dynamic values - Passing runtime values (username, password) from the feature file step to Java method arguments
@RunWith(Cucumber.class)— tells JUnit to use Cucumber as the test executor@CucumberOptions(features = "src/test/resources/features")— path to feature filesglue = "stepdefinitions"— package where step definitions and hooks are located
dryRun = true— validates every step has a matching method without executing anythingplugin = {"pretty"}— formatted Gherkin output in the consoleplugin = {"html:target/report.html"}— built-in HTML reportplugin = {"json:target/report.json"}— JSON output for third-party reporting toolstags— run only scenarios matching a tag expressionsnippets = SnippetType.CAMELCASE— camelCase method stub generation on undefined steps
- Adding tags to a
Scenario:@smoke,@regression,@sanity,@wip - Adding a tag to a
Feature— all scenarios inside inherit that tag automatically - Multiple tags on one scenario:
@smoke @regression— both apply independently
@smoke or @regression— run scenarios that have either tag (OR)@smoke and @regression— run only scenarios that have both tags (AND)not @smoke— exclude all@smokescenarios from this run- Combining operators:
(@smoke or @regression) and not @wip
Scenario Outline— repeat the entire scenario for every row in theExamplestableExamplestable — pipe-delimited (|); first row is the header used as placeholders- Placeholder syntax in steps:
<username>,<password>— replaced at runtime with each row's values - Use when: all steps in the scenario must run again for each dataset
DataTable— pass multiple values to a single step, not the whole scenario- Use when: only one step needs multiple inputs (e.g. entering a list of credentials)
dataTable.asList()— flatList<String>of all valuesdataTable.asLists()—List<List<String>>— rows and columnsdataTable.asMaps()—List<Map<String, String>>— header row becomes the key- Key interview question:
Scenario OutlinevsDataTable— what is the difference and when do you use each?
@Before— runs before every scenario; equivalent to@BeforeMethodin TestNG@After— runs after every scenario; receives optionalScenarioobject@BeforeAll— runs once before the entire suite; method must be static@AfterAll— runs once after the entire suite; method must be static@BeforeStep/@AfterStep— runs before/after each individual step
orderattribute — multiple hooks run in descending order (higher number runs first)- Conditional hooks:
@Before("@smoke")— runs only for scenarios tagged@smoke - Hooks run silently — not shown in built-in reports unless you add explicit log statements
- Hook class name is irrelevant — only the annotation matters;
gluepicks them up automatically scenario.isFailed()in@After— check if the scenario failed, then take a screenshot
- Adding
selenium-javaandwebdrivermanagerdependencies topom.xml - Initializing
WebDriverin@Before:WebDriverManager.chromedriver().setup(); driver = new ChromeDriver(); - Opening the application URL:
driver.get("https://...") - Closing the browser:
driver.quit()in@After - Static
WebDriverapproach — simple, works for single-thread runs; does not support parallel execution - Why Cucumber forbids
extendsbetween a hooks class and step definition class — leads to Dependency Injection
- Problem: multiple step definition classes all need the same
WebDriver, but Cucumber forbidsextends - DI concept: instead of each class creating its own object, one object is created and injected into all classes that need it
- Analogy — same as TestNG injecting
ITestResultinto@AfterMethodautomatically
- Add
cucumber-picocontainertopom.xml - Create
BaseDrivershared class — containspublic WebDriver driver(and laterExtentTest test) - Constructor injection in every class:
public LoginSteps(BaseDriver baseDriver) { this.baseDriver = baseDriver; } - PicoContainer automatically creates one
BaseDriverinstance per scenario and injects it everywhere - Access
WebDrivereverywhere viabaseDriver.driver— no static fields needed - Same pattern applies to
Hooks, step definition classes, page classes — all get the same instance
- Creating a
pagespackage — one class per application page (e.g.LoginPage,HomePage) @FindByannotations for element declaration;PageFactory.initElements(driver, this)for initialization- Page action methods:
enterUsername(String user),enterPassword(String pass),clickLoginButton() - Accessing page objects from step definitions:
new LoginPage(baseDriver.driver) - Storing commonly used page objects in
BaseDriverfor universal access - Framework folder structure —
features/,stepdefinitions/,pages/,hooks/— organised by module
- Add
extentreportsMaven dependency ExtentSparkReporter sparkReporter = new ExtentSparkReporter("target/extent-report.html")ExtentReports reports = new ExtentReports(); reports.attachReporter(sparkReporter);- Declare
static ExtentReports reportsandstatic ExtentTest testinBaseDriver— accessible everywhere - Initialize in
@BeforeAll(must bestatic); flush in@AfterAll:reports.flush()
- Create a test node per scenario:
baseDriver.test = reports.createTest(scenario.getName()) - Log steps:
baseDriver.test.info("Navigating to login page") - Mark results:
test.pass("Login successful")/test.fail("Login failed") - Add timestamp to report filename using
new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date())
- Check for failure in
@After:if (scenario.isFailed()) { ... } - Cast
WebDrivertoTakesScreenshot:((TakesScreenshot) baseDriver.driver).getScreenshotAs(OutputType.FILE) - Save to path using
scenario.getName()as filename:target/images/<scenarioName>.png - Attach to ExtentReport:
baseDriver.test.fail(MediaEntityBuilder.createScreenCaptureFromPath(imgPath).build())
Append new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()) to avoid overwriting previous report files
Trigger mvn test from a Jenkins Freestyle job; use HTML Publisher plugin to display ExtentReport inside Jenkins
Replace new ChromeDriver() with new RemoteWebDriver(new URL(hubUrl), options) to run on Grid or cloud
Push framework to GitHub; connect Jenkins to the repo for CI-triggered automated test runs on every commit
Explain: "PicoContainer creates one BaseDriver instance and injects it via constructor into every class — no static fields, no inheritance needed"
Background: visible, before only, file-scoped. Hooks: invisible, before+after, suite-wide, supports conditional tags and ordering