Implementing Step Definitions
Connect Gherkin feature files to executable code by writing step definitions using a BDD framework.
Implementing Step Definitions is a free Testing Mastery: JUnit, Mockito & Integration Tests lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Testing Mastery: JUnit, Mockito & Integration Tests learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Step Definitions: The Code Bridge
In Behavior-Driven Development (BDD), Gherkin feature files describe behavior in plain language. But how does your application understand these instructions?
This is where Step Definitions come in! They are the crucial link, translating each Gherkin step (Given, When, Then) into executable code.
- They are methods that match Gherkin patterns.
- BDD frameworks (like Cucumber) find and run these methods.
Our Example: A Simple Calculator
Let's use a basic Calculator to demonstrate how step definitions work. This class will hold the state and logic that our steps will interact with.
Try running this simple calculator:
public class Calculator {
private int result;
public void add(int a, int b) {
this.result = a + b;
}
public int getResult() {
return result;
}
public void clear() {
this.result = 0;
}
public static void main(String[] args) {
Calculator calc = new Calculator();
calc.add(10, 5);
System.out.println("10 + 5 = " + calc.getResult());
}
}The @Given Annotation
The @Given annotation is used for methods that establish the initial context or preconditions for a scenario. It sets up the 'world' before an action takes place.
We'll create a StepDefinitions class to house our step methods. Notice how the string in @Given matches a Gherkin step.
import io.cucumber.java.en.Given;
public class StepDefinitions {
private Calculator calculator;
@Given("I have a calculator")
public void iHaveACalculator() {
calculator = new Calculator();
System.out.println("Given: Calculator initialized.");
}
public static void main(String[] args) {
StepDefinitions steps = new StepDefinitions();
steps.iHaveACalculator();
}
}The @When Annotation
The @When annotation marks methods that describe an action or event. This is typically where the system under test is interacted with or triggered.
Our calculator will perform an 'add' operation here.
import io.cucumber.java.en.Given;
import io.cucumber.java.en.When;
public class StepDefinitions {
private Calculator calculator;
@Given("I have a calculator")
public void iHaveACalculator() {
calculator = new Calculator();
System.out.println("Given: Calculator initialized.");
}
@When("I add two numbers")
public void iAddTwoNumbers() {
// For now, let's hardcode values. We'll make it dynamic soon!
calculator.add(10, 20);
System.out.println("When: Added two numbers (10, 20).");
}
public static void main(String[] args) {
StepDefinitions steps = new StepDefinitions();
steps.iHaveACalculator();
steps.iAddTwoNumbers();
}
}The @Then Annotation
The @Then annotation is for methods that observe the outcome of the action. This is where you assert that the system behaved as expected.
We'll check if the calculator's result is correct.
import io.cucumber.java.en.Given;
import io.cucumber.java.en.When;
import io.cucumber.java.en.Then;
public class StepDefinitions {
private Calculator calculator;
@Given("I have a calculator")
public void iHaveACalculator() {
calculator = new Calculator();
System.out.println("Given: Calculator initialized.");
}
@When("I add two numbers")
public void iAddTwoNumbers() {
calculator.add(10, 20);
System.out.println("When: Added two numbers (10, 20).");
}
@Then("the result should be 30")
public void theResultShouldBe30() {
if (calculator.getResult() == 30) {
System.out.println("Then: Result is 30. Test PASSED!");
} else {
System.out.println("Then: Expected 30, got " + calculator.getResult() + ". Test FAILED!");
}
}
public static void main(String[] args) {
StepDefinitions steps = new StepDefinitions();
steps.iHaveACalculator();
steps.iAddTwoNumbers();
steps.theResultShouldBe30();
}
}Passing Data with Arguments
Hardcoding values isn't flexible. BDD frameworks allow you to capture data from the Gherkin step and pass it as arguments to your step definition method.
We use regular expressions or Cucumber Expressions (like {int}) to define placeholders.
import io.cucumber.java.en.Given;
import io.cucumber.java.en.When;
import io.cucumber.java.en.Then;
public class StepDefinitions {
private Calculator calculator;
@Given("I have a calculator")
public void iHaveACalculator() {
calculator = new Calculator();
System.out.println("Given: Calculator initialized.");
}
@When("I add {int} and {int}")
public void iAddTwoNumbers(int num1, int num2) {
calculator.add(num1, num2);
System.out.println("When: Added " + num1 + " and " + num2 + ".");
}
@Then("the result should be {int}")
public void theResultShouldBe(int expectedResult) {
if (calculator.getResult() == expectedResult) {
System.out.println("Then: Result is " + expectedResult + ". Test PASSED!");
} else {
System.out.println("Then: Expected " + expectedResult + ", got " + calculator.getResult() + ". Test FAILED!");
}
}
public static void main(String[] args) {
StepDefinitions steps = new StepDefinitions();
steps.iHaveACalculator();
steps.iAddTwoNumbers(5, 7); // Simulating Gherkin "When I add 5 and 7"
steps.theResultShouldBe(12); // Simulating Gherkin "Then the result should be 12"
}
}Flexible Step Matching with Regex
While {int} and {string} are convenient, you can use full regular expressions for more complex matching patterns.
For example, if your Gherkin step was Given I have an (empty|full) wallet, your step definition pattern might be "^I have an (empty|full) wallet$" to capture 'empty' or 'full'.
- Use
\\d+for one or more digits. - Use
"(.*)"to capture any text.
This allows a single step definition to match several slightly different Gherkin phrases.
BDD Hooks: Setup & Teardown
Sometimes, you need to perform actions before or after each scenario, or even before/after all features. BDD frameworks provide Hooks for this.
@Before: Runs before each scenario. Useful for resetting state.@After: Runs after each scenario. Good for cleanup.
import io.cucumber.java.en.Given;
import io.cucumber.java.en.When;
import io.cucumber.java.en.Then;
import io.cucumber.java.Before;
import io.cucumber.java.After;
public class StepDefinitions {
private Calculator calculator;
@Before
public void setupScenario() {
System.out.println("\n--- Scenario Setup (@Before) ---");
calculator = new Calculator(); // Ensure a fresh calculator for each scenario
}
@After
public void teardownScenario() {
System.out.println("--- Scenario Teardown (@After) ---");
// Optional: add cleanup if needed, e.g., closing resources
}
@Given("I have a calculator")
public void iHaveACalculator() {
System.out.println("Given: Calculator initialized.");
}
@When("I add {int} and {int}")
public void iAddTwoNumbers(int num1, int num2) {
calculator.add(num1, num2);
System.out.println("When: Added " + num1 + " and " + num2 + ".");
}
@Then("the result should be {int}")
public void theResultShouldBe(int expectedResult) {
if (calculator.getResult() == expectedResult) {
System.out.println("Then: Result is " + expectedResult + ". Test PASSED!");
} else {
System.out.println("Then: Expected " + expectedResult + ", got " + calculator.getResult() + ". Test FAILED!");
}
}
public static void main(String[] args) {
StepDefinitions steps = new StepDefinitions();
// Simulate a scenario run
steps.setupScenario();
steps.iHaveACalculator();
steps.iAddTwoNumbers(8, 4); // Simulating Gherkin "When I add 8 and 4"
steps.theResultShouldBe(12); // Simulating Gherkin "Then the result should be 12"
steps.teardownScenario();
// Simulate another scenario run
steps.setupScenario();
steps.iHaveACalculator();
steps.iAddTwoNumbers(10, 0); // Simulating Gherkin "When I add 10 and 0"
steps.theResultShouldBe(10); // Simulating Gherkin "Then the result should be 10"
steps.teardownScenario();
}
}Running Your Feature Files
In a real BDD project, you wouldn't manually call step definition methods like we did in main.
Instead, you'd have a Test Runner (often a JUnit test class) that tells the BDD framework (e.g., Cucumber) where to find your feature files and step definitions.
The runner then parses the Gherkin, finds matching step definitions, and executes them in order, reporting the results.
Quick Check: Step Definition Roles
Match the BDD annotation to its primary role in a step definition.
Recap: Implementing Step Definitions
You've learned how to connect human-readable Gherkin steps to executable code!
- Step Definitions are Java methods annotated with
@Given,@When, or@Then. - They use regular expressions or Cucumber Expressions (like
{int}) to match Gherkin text. - Arguments can be captured from the Gherkin step and passed to the method.
- Hooks like
@Beforeand@Afterhelp manage scenario setup and teardown.
This bridge between language and code is fundamental to BDD, ensuring tests are understandable by everyone involved in the project!
Frequently asked questions
Is the “Implementing Step Definitions” lesson free?
Yes — the full text of “Implementing Step Definitions” is free to read here on the web, and the Testing Mastery: JUnit, Mockito & Integration Tests course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Testing Mastery: JUnit, Mockito & Integration Tests course, upgrade to CoddyKit PRO.
What will I learn in “Implementing Step Definitions”?
Connect Gherkin feature files to executable code by writing step definitions using a BDD framework. You practise Testing Mastery: JUnit, Mockito & Integration Tests with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Testing Mastery: JUnit, Mockito & Integration Tests?
No prior experience is required. Testing Mastery: JUnit, Mockito & Integration Tests on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Implementing Step Definitions” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Testing Mastery: JUnit, Mockito & Integration Tests lesson?
Yes. Every Testing Mastery: JUnit, Mockito & Integration Tests lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Introduction to BDD
- Gherkin Syntax and Features
- Implementing Step Definitions
- Scenario Outlines and Data Tables