Skip to content

Add Step info to @BeforeStep and @AfterStep hooks#3139

Merged
mpkorstanje merged 13 commits intocucumber:mainfrom
glaciousm:feature/step-info-in-step-hooks
Jan 27, 2026
Merged

Add Step info to @BeforeStep and @AfterStep hooks#3139
mpkorstanje merged 13 commits intocucumber:mainfrom
glaciousm:feature/step-info-in-step-hooks

Conversation

@glaciousm
Copy link
Copy Markdown
Contributor

Summary

This PR implements the feature requested in #1805 - allowing @BeforeStep and @AfterStep hooks to receive step information (keyword, text, line number) alongside the existing Scenario parameter.

New API:

@BeforeStep
public void beforeStep(Scenario scenario, Step step) {
    System.out.println("Running: " + step.getKeyword() + step.getText());
    System.out.println("At line: " + step.getLine());
}

@AfterStep  
public void afterStep(Scenario scenario, Step step) {
    if (scenario.isFailed()) {
        System.out.println("Failed step: " + step.getText());
    }
}

Changes

  • Add new io.cucumber.java.Step interface exposing step details
  • Add internal StepInfo wrapper class implementing the Step interface
  • Extend HookDefinition with execute(state, step) default method
  • Update JavaHookDefinition to support 2-parameter step hooks
  • Pass step info through HookTestStep execution chain

Backward Compatibility

  • Existing hooks with 0 or 1 parameter continue to work unchanged
  • No breaking changes to the public API

Test plan

  • All existing cucumber-core tests pass (665 tests)
  • All existing cucumber-java tests pass (115 tests)
  • Tested with a real Selenium/Cucumber project

Closes #1805

Allow @BeforeStep and @afterstep hooks to receive step information
(keyword, text, line number) alongside the existing Scenario parameter.

New API:
```java
@BeforeStep
public void beforeStep(Scenario scenario, Step step) {
    System.out.println("Running: " + step.getKeyword() + step.getText());
    System.out.println("At line: " + step.getLine());
}
```

Changes:
- Add new io.cucumber.java.Step interface exposing step details
- Add StepInfo wrapper class implementing Step interface
- Extend HookDefinition with execute(state, step) default method
- Update JavaHookDefinition to support 2-parameter step hooks
- Pass step info through HookTestStep execution chain
- Maintain full backward compatibility with existing hooks

Closes cucumber#1805
Copy link
Copy Markdown
Contributor

@mpkorstanje mpkorstanje left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall this looks like a decent approach.

  • The HookTestStep::run method is duplicated. I don't think that this is necessary. In TestStep::executeStep it should be possible to pass the step argument and propagate it down to HookDefinition::execute by adding an extra argument to StepDefinitionMatch::runStep. If it goes unused in other places, that is okay.

  • io.cucumber.plugin.event.StepArgument shouldn't be visible to users but I don't know what to request that yet.

Comment thread cucumber-core/src/main/java/io/cucumber/core/runner/HookTestStep.java Outdated
Comment thread cucumber-core/src/main/java/io/cucumber/core/runner/HookDefinitionMatch.java Outdated
Comment thread cucumber-java/src/main/java/io/cucumber/java/JavaHookDefinition.java Outdated
Comment thread cucumber-java/src/main/java/io/cucumber/java/Step.java Outdated
@mpkorstanje
Copy link
Copy Markdown
Contributor

Don't worry about the Semver check btw.

- Remove duplicated run method from HookTestStep, use parent's implementation
- Pass step argument through TestStep::executeStep and ExecutionMode
- Add runStep(state, step) default method to StepDefinitionMatch interface
- Make HookDefinition.execute(state) a default no-op and deprecate it
- Remove getArgument() from Step interface (StepArgument visibility concern)
- Refactor JavaHookDefinition validation to explicitly check hook types
- Update tests to match new error message format
@glaciousm
Copy link
Copy Markdown
Contributor Author

pushed changes based on the comments, if you could re-review

Copy link
Copy Markdown
Contributor

@mpkorstanje mpkorstanje left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please review your own code before asking me for a review.

  1. These changes came in incredibly fast.
  2. The changes are as requested in letter, but not in spirit. I find that I'm writing the same comments as I did in my previous review, but for exactly one level higher in the call chain.
  3. There is a rather obvious blunder in the code that regular refactoring tools wouldn't produce.

Normally I'd give you the benefit of the doubt but I do see several LLM based projects on your profile so I am skeptical that you've done the necessary work to create a correct pull request.

@glaciousm
Copy link
Copy Markdown
Contributor Author

glaciousm commented Jan 15, 2026

I will go through all my changes, one by one, apologies

- StepDefinitionMatch.runStep() now takes Step parameter directly
- ExecutionMode.execute() takes Step parameter
- TestStep.run() takes Step parameter
- Step flows through: TestStep → ExecutionMode → StepDefinitionMatch → HookDefinition
@glaciousm
Copy link
Copy Markdown
Contributor Author

@mpkorstanje i did made some changes but i need your opinion here.. i really dont like the ,null in the step.run, i would prefer overloading but i was concerned you would see it as duplication. What is your preference? Overload the run or the ,null is fine?

@mpkorstanje
Copy link
Copy Markdown
Contributor

mpkorstanje commented Jan 15, 2026

I think null values for method arguments and constructors are fine. Especially in internal APIs. Just don't return them.

I'm currently working on adding nullability information with Jspecify. So then the compiler will be able to check once that lands.

@glaciousm
Copy link
Copy Markdown
Contributor Author

In this case if you can review once more it would be great. As for the nulluability thats would be super helpful because null pointers in cucumber debuging is a pain..

Comment thread cucumber-java/src/main/java/io/cucumber/java/JavaHookDefinition.java Outdated
Copy link
Copy Markdown
Contributor

@mpkorstanje mpkorstanje left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks better. I'm going too need to take a closer look at the inheritance around TestStep.

@mpkorstanje mpkorstanje self-assigned this Jan 20, 2026
Copy link
Copy Markdown
Contributor

@mpkorstanje mpkorstanje left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the TestCaseState to keep track of the current active gherkin step analogous to the currently active test step (hooks or steps) the solution simplifies a lot. I've pushed that as a change.

Please do update the cucumber-java/README.md with some documentation. And some tests to verify everything works as expected. The refactoring of the hook definition validation introduced several branches that are not covered. And the feature itself is untested.

@glaciousm
Copy link
Copy Markdown
Contributor Author

Added the requested documentation and tests:

  • Updated cucumber-java/README.md with documentation for the Step parameter in @BeforeStep/@AfterStep hooks
  • Added unit tests in JavaHookDefinitionTest covering the validation branches for step hooks
  • Added integration tests (step-hooks.feature + StepHooksSteps.java) to verify the feature works end-to-end

Ready for review when you have time.

@mpkorstanje mpkorstanje self-requested a review January 26, 2026 22:32
@mpkorstanje mpkorstanje merged commit b235d46 into cucumber:main Jan 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Step information in @BeforeStep and @AfterStep hook

2 participants