Glue Code Concept in Cucumber JVM

Glue code is the part of a Cucumber JVM framework that connects Gherkin feature files with their Java implementation. In practical terms, it is not one special file named glue. It is the collection of Java packages that contain step definition classes and hook classes. These packages tell Cucumber where to find the Java methods that should run when it reads a Gherkin step, and where to find lifecycle methods that should run before or after scenarios.

The idea is simple but extremely important. Feature files describe behavior in business-readable language. Step definitions and hooks contain Java code that makes that behavior executable. Glue code joins these two sides together. Without glue configuration, Cucumber can still read feature files, but it cannot know where the Java implementation lives. As a result, steps remain undefined, hooks do not run, and the automation framework cannot execute scenarios successfully.

Understanding glue code is essential for anyone working with Cucumber JVM. Many beginner errors come from wrong glue paths, missing hook packages, misplaced step definition classes, duplicate step definitions, or confusion between feature file location and glue location. A clean glue configuration makes execution predictable. A messy glue setup creates undefined steps, ambiguous matches, missing setup, skipped teardown, and painful debugging.

1. What Is Glue Code?

Glue code is the Java-side connection between Gherkin scenarios and automation implementation. It consists mainly of step definitions and hooks that Cucumber scans during execution. When Cucumber reads a feature file and finds a step such as "Given the user is logged in", it searches the configured glue packages for a Java method whose annotation matches that step. If it finds a match, it executes that method.

Glue code acts as a bridge between business-readable scenarios written in Gherkin and executable Java automation code. The feature file describes what should happen. The step definition explains how the scenario step should be executed. The hook classes manage setup, teardown, screenshots, cleanup, and other lifecycle actions.

At a high level, the relationship looks like this:

Feature File
      |
      v
Glue Code
      |
      v
Step Definitions / Hooks
      |
      v
Automation Code

This does not mean glue code should contain all automation logic. Glue code should connect and coordinate. Page Objects, API service classes, database helpers, utilities, and managers should perform the actual low-level automation work. Glue is the discovery and connection layer.

Glue Code Is a Discovery Concept

The word glue is mainly about discovery. Cucumber needs to discover which Java classes contain executable step mappings and lifecycle hooks. The glue configuration tells Cucumber where to search. If the configured packages are correct, Cucumber can find step definitions and hooks. If they are wrong, the framework appears broken even if the Java code exists somewhere else in the project.

2. Why Is It Called Glue Code?

The term glue comes from the idea of joining two separate components together. In Cucumber, the two components are Gherkin feature files and Java automation code. Feature files contain the business-readable what. Step definitions contain the executable how. Glue code connects them so that scenarios can actually run.

For example, a feature file may say:

Given the user has valid credentials
When the user logs in
Then the dashboard should be displayed

These steps are readable, but they are not executable by themselves. Java methods must implement them. The glue configuration tells Cucumber where those methods are located. That is why the configured packages are called glue. They glue Gherkin text to Java methods.

The name also reminds us that glue code should not dominate the framework. Glue should hold the parts together. It should not become the whole application. If step definitions inside glue packages contain all Selenium code, API logic, database queries, file reading, reporting, and business logic, the framework becomes hard to maintain. Good glue is thin and well-organized.

3. Purpose of Glue Code

The main purpose of glue code is to help Cucumber locate and execute the Java classes that belong to scenario execution. It helps Cucumber find step definitions, find hooks, match Gherkin steps to Java methods, execute automation code, and manage scenario lifecycle events.

  • Locate step definition classes.
  • Locate hook classes.
  • Match Gherkin steps to annotated Java methods.
  • Execute automation code through step definitions.
  • Manage scenario lifecycle through hooks.

Glue configuration is especially important in larger frameworks. A small project may have only one stepdefinitions package and one hooks package. A large project may have common steps, module-specific steps, API steps, UI steps, hooks, and shared support classes. Cucumber must know exactly which packages to scan. Without clear glue organization, execution becomes unpredictable.

Glue Code Supports Framework Boundaries

Glue code also reinforces architecture boundaries. Step definition and hook packages are glue. Page objects, utilities, services, factories, and managers are used by glue code, but they are not usually considered glue themselves. This distinction helps teams avoid placing every framework class inside the glue path unnecessarily.

4. Where Is Glue Code Located?

Glue code is located in Java packages that contain step definitions and hooks. It is not a separate file. In a typical Maven Cucumber JVM project, these packages live under `src/test/java`. The feature files usually live under `src/test/resources/features`, while the glue code packages live under the Java test source area.

src/test/java
|
|-- stepdefinitions
|   |-- LoginSteps.java
|   |-- PaymentSteps.java
|
|-- hooks
|   |-- Hooks.java

In this example, `stepdefinitions` and `hooks` together form the glue code. The runner can point to these packages using the `glue` option. Cucumber will scan those packages for annotated step definition methods and hook methods.

In real projects, package names may include full company and project paths, such as `com.company.project.stepdefinitions` and `com.company.project.hooks`. The concept remains the same. Glue points to the packages that contain executable Cucumber bindings.

Glue Code vs Supporting Code

Supporting code such as Page Objects, service classes, database helpers, configuration readers, and utilities may live nearby, but they are not necessarily glue packages. Cucumber does not need to scan Page Objects for step annotations if those classes do not contain Cucumber annotations. Keeping glue packages focused improves clarity.

5. How Glue Is Configured

Glue packages are configured in the Cucumber runner. In many JUnit 4 Cucumber JVM projects, this is done through `@CucumberOptions`. The `features` option tells Cucumber where feature files are located. The `glue` option tells Cucumber where step definitions and hooks are located.

@RunWith(Cucumber.class)
@CucumberOptions(
    features = "src/test/resources/features",
    glue = {"stepdefinitions", "hooks"}
)
public class TestRunner {
}

Here, `stepdefinitions` and `hooks` are glue packages. When the runner starts, Cucumber reads feature files from the features location and searches the glue packages for matching Java annotations. If the package names are misspelled or incomplete, Cucumber will not find the expected classes.

In projects with full package names, the runner may look like this:

@CucumberOptions(
    features = "classpath:features",
    glue = {
        "com.company.project.stepdefinitions",
        "com.company.project.hooks"
    }
)

Using full package names is common in enterprise projects because it avoids ambiguity and matches normal Java package conventions.

Some teams prefer configuring glue at a common root package, such as `com.company.project`. This allows Cucumber to discover step definitions and hooks under nested packages like `com.company.project.stepdefinitions.login` and `com.company.project.hooks`. This can be convenient, but it should be used carefully. If the root package is too broad, Cucumber may scan many classes that do not need to be part of glue. A better root is usually the smallest package that contains only Cucumber bindings and lifecycle classes.

Runner configuration should be treated as framework code, not a one-time setup detail. When packages are renamed, modules are split, or hooks are moved, the glue option should be reviewed immediately. Many undefined step errors are caused not by missing step definitions, but by stale glue configuration.

6. How Glue Code Works Internally

When execution starts, the runner loads Cucumber configuration. Cucumber reads the feature file location, parses feature files, and identifies scenarios and steps. For each step, it searches only inside the configured glue packages. It does not scan the entire project randomly. This is why glue configuration is so important.

Suppose Cucumber reads this step:

Given the user is logged in

The execution flow is:

Runner
     |
     v
Feature File
     |
     v
Read Given Step
     |
     v
Search Glue Packages
     |
     v
Find Matching Step Definition
     |
     v
Execute Java Method

If Cucumber finds one matching method, the step executes. If it finds no matching method, the step is undefined. If it finds more than one matching method, the step is ambiguous. Both undefined and ambiguous steps often point back to glue organization, step wording, or annotation patterns.

Glue Search Scope

The search scope is limited to the configured glue packages. This keeps execution efficient and predictable, but it also means classes outside glue are invisible to Cucumber as step definitions or hooks. A LoginSteps class in the wrong package will not be discovered unless the runner glue includes that package.

This limited search scope is useful because it prevents Cucumber from scanning the entire project blindly. Large frameworks may contain page objects, API models, service clients, utilities, configuration classes, and test data builders. Most of those classes do not contain Cucumber annotations and do not need to be scanned. A focused glue path keeps discovery clean and reduces the chance of unexpected matches.

At the same time, the limited scope means package discipline matters. If a developer creates a new step definition class under a package that is not included in glue, Cucumber will not find it. This is why teams should document where step definitions and hooks must be created.

7. Glue Code Components

Glue code usually contains two major components: step definitions and hooks. Step definitions implement Gherkin steps. Hooks manage the scenario lifecycle. Both are discovered through the glue configuration.

A step definition example:

@Given("the user is logged in")
public void userLoggedIn() {
    loginAction.loginAsDefaultUser();
}

Step definitions may call page objects, API services, utilities, data builders, or assertion helpers. They implement the executable meaning of Gherkin steps.

A hook example:

@Before
public void setup() {
    driverFactory.createDriver();
}

@After
public void teardown() {
    driverFactory.quitDriver();
}

Hooks are used for browser setup, browser cleanup, screenshot capture, logging, report attachments, test data cleanup, and other lifecycle tasks. Because hooks are also discovered through glue, forgetting the hooks package in glue configuration means hooks will not run.

Hooks Are Part of Glue

Many beginners remember to include step definition packages but forget hook packages. This leads to confusing behavior. Steps may execute, but browsers may not open, screenshots may not attach, or cleanup may not happen. Hooks are part of glue and must be included in the glue path.

8. Glue Code vs Step Definition

Many beginners think glue code and step definitions are the same thing. They are related, but they are not exactly the same. A step definition is an individual Java method mapped to one Gherkin step. Glue code is the collection of packages that Cucumber scans for step definitions and hooks.

Glue Code Step Definition
Collection of packages Individual Java method
Includes hooks Does not include hooks by itself
Used by Cucumber for discovery Used to implement one step
Configured in runner Located inside a glue package

This distinction is important in interviews and real debugging. If someone says the glue path is wrong, they mean Cucumber cannot find the packages that contain step definitions and hooks. If someone says a step definition is wrong, they usually mean the individual method, annotation, or implementation has a problem.

9. Example Project Structure

A typical Cucumber JVM project may look like this:

src
|
|-- test
|   |-- java
|   |   |-- runners
|   |   |   |-- TestRunner.java
|   |   |
|   |   |-- stepdefinitions
|   |   |   |-- LoginSteps.java
|   |   |   |-- OrderSteps.java
|   |   |
|   |   |-- hooks
|   |   |   |-- Hooks.java
|   |   |
|   |   |-- pages
|   |   |   |-- LoginPage.java
|   |   |
|   |   |-- utilities
|
|-- test
    |-- resources
        |-- features
            |-- login.feature

The glue configuration may be:

glue = {"stepdefinitions", "hooks"}

In this structure, Cucumber scans `stepdefinitions` and `hooks`. It does not need to scan `pages` or `utilities` unless those packages also contain Cucumber annotations, which they usually should not. Step definitions can still call page and utility classes. They simply do not need to be discovered as glue.

10. What Happens If Glue Is Incorrect?

If glue is incorrect, Cucumber cannot find the Java implementation. For example, suppose the runner contains:

glue = {"steps"}

but the actual package is:

stepdefinitions

Cucumber will search the `steps` package and fail to find methods in `stepdefinitions`. The result is usually undefined steps or missing hook behavior. None of the scenarios will execute successfully until the glue path is corrected.

Incorrect glue can also happen after refactoring. A team may rename packages, move classes, or introduce full package names but forget to update the runner. The Java code may compile, but Cucumber still cannot discover it. Whenever step packages or hook packages move, runner glue configuration must be reviewed.

Common Symptoms of Wrong Glue

  • Steps are reported as undefined even though Java methods exist.
  • Before and After hooks do not run.
  • Screenshots are not captured on failure.
  • Browser setup or cleanup is skipped.
  • Cucumber suggests snippets for steps already implemented elsewhere.

11. Multiple Glue Packages

Large frameworks often use multiple glue packages. For example:

@CucumberOptions(
    glue = {
        "stepdefinitions",
        "hooks",
        "commonsteps"
    }
)

Cucumber searches all specified packages. This is useful when the framework has common reusable steps, module-specific steps, API steps, or separated hook packages. However, multiple glue packages should be managed carefully. Too many broad packages can make step discovery harder to reason about and can increase the risk of duplicate matching patterns.

A good approach is to keep glue modular but predictable. Common steps can live in `commonsteps`. Domain steps can live in clear module packages. Hooks can live in `hooks`. Avoid adding unrelated packages such as pages, utilities, managers, or models to glue unless they contain actual Cucumber annotations.

Module-Specific Glue

In some enterprise frameworks, teams use module-specific runners with module-specific glue. For example, a payments runner may scan payment step definitions and common hooks, while an orders runner scans order step definitions and common hooks. This can reduce the discovery area and clarify ownership. The tradeoff is that runner configuration becomes more complex. If a shared step is needed by several modules, it should live in a common glue package that those runners include.

Module-specific glue works best when the project has strong package conventions. Without conventions, teams may duplicate shared steps in multiple module packages, which leads to ambiguity and maintenance problems. Shared steps should be deliberately designed, not copied casually.

12. Glue Package Search Process

Internally, Cucumber performs a search across configured glue packages. It reads a step, checks the first glue package, checks the next, continues through configured packages, finds a matching annotation, and executes the method. If no match exists, the step is undefined.

Feature File
      |
      v
Read Step
      |
      v
Search Package 1
      |
      v
Search Package 2
      |
      v
Search Package 3
      |
      v
Find Matching Annotation
      |
      v
Execute Method

The order of packages is less important than the uniqueness and clarity of matching patterns. Cucumber needs one clear match. If two packages contain step definitions that match the same text, ambiguity occurs. This is why step vocabulary and package organization matter.

Ambiguity and Glue

Ambiguity is not always caused by glue, but glue can expose it. If the runner scans multiple packages containing similar generic patterns, Cucumber may find more than one match. To fix ambiguity, remove duplicate step definitions, make patterns more specific, or standardize wording so one behavior has one phrase.

13. Glue Code and Package Structure

A good project keeps glue code organized by business modules. Step definitions should be grouped by feature area rather than by annotation type. For example:

stepdefinitions
|
|-- LoginSteps.java
|-- RegistrationSteps.java
|-- PaymentSteps.java
|-- OrderSteps.java
|-- ProfileSteps.java

This structure keeps related Given, When, and Then steps together. A login flow can be maintained in LoginSteps. Payment behavior can be maintained in PaymentSteps. Order behavior can be maintained in OrderSteps. This is easier than splitting steps into GivenSteps, WhenSteps, and ThenSteps, which scatters one business flow across several files.

Avoid a single large file such as:

Steps.java

A single file becomes difficult to maintain as the suite grows. It encourages unrelated steps to accumulate in one place. It also increases the chance of duplicate wording, unclear method names, and merge conflicts.

Modular Glue Packages

For very large frameworks, teams may organize glue packages by domain, such as `stepdefinitions.login`, `stepdefinitions.orders`, and `stepdefinitions.payments`. This can work well if the runner includes the correct root package or all needed module packages.

14. Common Glue Code Mistakes

Several mistakes appear frequently in Cucumber JVM projects. The first is using the wrong package name in glue configuration. For example:

glue = {"step"}

instead of:

glue = {"stepdefinitions"}

Another mistake is forgetting the hooks package. If the runner includes only stepdefinitions while hooks are inside a separate hooks package, hooks never execute. Browser setup, screenshots, cleanup, and reporting attachments may silently fail or never happen.

A third mistake is placing step definitions outside the configured glue path. For example, LoginSteps may be inside an `automation` package while the runner scans only `stepdefinitions`. Cucumber will never find those steps. Very large glue packages are another issue. Having 100 or more step definition classes inside one flat package can become difficult to manage. Organize them by module.

  • Wrong package name in glue configuration.
  • Forgetting to include the hooks package.
  • Placing step definitions outside the glue path.
  • Adding unrelated packages to glue.
  • Keeping too many unrelated step classes in one flat package.
  • Creating duplicate step definitions across glue packages.

Troubleshooting Glue Problems

When Cucumber reports undefined steps, first confirm that the step definition class exists and compiles. Then check whether the package containing that class is included in the runner glue. Next, compare the annotation text with the Gherkin step text. If all of those are correct, check whether the runner being executed is the runner you expect. In large projects, local execution and CI execution may use different runners.

When hooks do not run, check whether the hook class package is included in glue and whether the hook annotations come from the correct Cucumber package. Also check tags if tagged hooks are used. A tagged hook only runs for scenarios with matching tags, so the hook may be configured correctly but filtered by tag conditions.

When ambiguity appears, search the glue packages for duplicate or overlapping step patterns. Ambiguity is often caused by a generic expression such as `the user {string}` matching the same text as a more specific step. Tightening the expression or standardizing wording usually fixes the problem.

15. Best Practices

Keep glue packages small, modular, and meaningful. Separate step definitions and hooks into dedicated packages. Configure the glue option correctly in the runner. Group step definition classes by business feature, not by annotation type. Avoid placing Page Objects, utilities, services, factories, or managers inside glue packages unless they contain Cucumber annotations, which they usually should not.

Keep step definitions thin and delegate actual automation to Page Objects or service classes. Glue code should coordinate execution, not become the entire automation framework. A step definition can call `loginPage.login()` or `orderService.createOrder()`. The technical details should remain in those lower layers.

Review glue configuration whenever packages are renamed or moved. In CI/CD environments, ensure the same runner and package structure are used as local execution. Document glue package conventions in the README so new contributors know where to place step definitions and hooks.

  • Keep glue packages small and modular.
  • Separate step definitions and hooks into dedicated packages.
  • Configure the glue option correctly in the runner.
  • Group step classes by business feature or module.
  • Do not place Page Objects or utilities in glue just for discovery.
  • Keep step definitions thin and delegate automation logic.

What Should Not Be Glue

Page Objects, utilities, models, constants, factories, managers, and service classes are usually not glue. They support glue code, but Cucumber does not need to discover them directly. Keeping these classes outside glue packages helps preserve clean architecture. It also makes it clearer which classes are Cucumber bindings and which classes are reusable framework components.

If a project places everything under one glue package, the boundary becomes blurry. New contributors may add locators, API clients, and utility methods directly into step classes because they cannot see a clear separation. A clean package structure teaches the intended architecture by example.

16. Real-Time Example

Consider a login feature file:

Feature: Login

Scenario: Successful login
  Given the user is on the login page
  When the user logs in with valid credentials
  Then the dashboard should be displayed

The runner may be configured like this:

@RunWith(Cucumber.class)
@CucumberOptions(
    features = "src/test/resources/features",
    glue = {"stepdefinitions", "hooks"}
)
public class TestRunner {
}

The step definition may live in the stepdefinitions package:

package stepdefinitions;

import io.cucumber.java.en.Given;

public class LoginSteps {

    @Given("the user is on the login page")
    public void userIsOnLoginPage() {
        loginPage.open();
    }
}

During execution, Cucumber reads the feature file, uses the runner configuration, searches the glue packages, finds the matching step definition, calls the Java method, and the method delegates to the page object. The page object interacts with the browser.

Feature File
      |
      v
Runner
      |
      v
Glue Package (stepdefinitions, hooks)
      |
      v
Matching Step Definition
      |
      v
Page Object
      |
      v
Browser

This example shows the correct responsibility split. The feature file describes login behavior. The runner configures discovery. Glue tells Cucumber where the Java bindings live. The step definition coordinates execution. The page object performs browser automation.

17. Glue Code in CI/CD

Glue configuration must work consistently in CI/CD pipelines. A common problem is that tests run locally but fail in Jenkins or GitHub Actions because the runner, package structure, or build command differs. If the CI job uses a different runner, it may use different glue configuration. If packages were renamed but the CI branch has stale configuration, steps may become undefined.

To avoid this, keep runner configuration under source control, avoid local-only paths, and prefer package names that match the actual Java structure. Build logs should show which runner or profile is being used. If undefined steps appear only in CI, compare the branch, runner, glue configuration, and compiled test classes.

CI pipelines should also publish useful failure output. Undefined step failures and hook failures should be visible in console logs and reports. If the build hides Cucumber output, glue configuration issues take longer to diagnose. A well-configured pipeline makes discovery problems obvious quickly.

18. Interview-Ready Summary

In interviews, a strong answer should explain that glue code is the bridge between Gherkin feature files and Java automation code. It consists of the packages containing step definitions and hooks. The `glue` attribute in `@CucumberOptions` tells Cucumber where to find these classes. During execution, Cucumber searches the glue packages for methods whose annotations match Gherkin steps.

You should also explain what happens when glue is wrong. If the glue path is incorrect, steps remain undefined and hooks are not executed. Good glue organization keeps step definitions and hooks in clear packages, groups steps by business module, and keeps automation logic delegated to page objects, services, or utilities.

  • Glue code bridges Gherkin feature files and Java automation.
  • It consists of packages containing step definitions and hooks.
  • The glue option tells Cucumber where to discover these classes.
  • Cucumber searches glue packages for matching annotations.
  • Wrong glue paths cause undefined steps and missing hooks.

19. Golden Rule

The golden rule is simple: glue code does not perform all automation; it connects Gherkin steps to the Java classes that perform automation. Feature files describe behavior. Glue packages expose step definitions and hooks. Step definitions coordinate execution. Page Objects, API services, database helpers, and utilities perform the detailed work.

When glue code is organized well, Cucumber JVM frameworks are easier to understand, execute, and maintain. When glue is misconfigured or overloaded, scenarios become undefined, hooks disappear, and automation logic becomes tangled. Correct glue configuration is one of the simplest but most important foundations of a stable Cucumber framework.