Unused Definitions with Behat Static Analysis

This post was updated in November 2025 with fresh know-how.
What is new?

Updated to new package name and command names.


Recently, I've been working on projects with Behat tests. There are hundreds of definitions that can be used in feature file scenarios.

I accidentally noticed that one of the definitions is not used at all and could be removed. This would result in less code to maintain, less code to read, and less code to upgrade.

But I thought, "That's weird." Why did not Behat report this definition in our CI? Oh, because there is no Behat static analysis report out of the box. Let's fix that.

Behat definitions are marked with annotations:

/**
 * @When I do something
 */
public function doSomething(): void
{
    //...
}

...or with PHP 8.0 attributes:

use Behat\Step\Then;

#[Then('I see light')]
public function seeTheLight(): void
{
    //...
}

These definitions can be used in *.feature files:

Given I do something
 Then I see light

But as the project develops, there feature files can change:

 Given I do something
-Then I see light
+Then I see green

Now we should remove the "I see light" definition because it's not used anymore. But we often focus on business code and testing and don't have time to check this.

This is ideal work for static analysis!

Behat Static Analysis

Why static analysis? We could have a Behat extension, that would run tests, compare used definitions, and report issues in the end. The problem is that Behat tests are extremely slow and we don't want to bind the tool with a specific Behat version.

We're lazy. We want to run a single command on any Behat version and get fast reliable feedback within a couple of seconds.

What do we actually analyze?

The process is simple:

Then there are 3 rules that it checks:


The tool output looks like this:

Found 127 Context and 225 feature files
Extracting definitions masks...

Found 1367 masks:
 * 863 exact
 * 204 /regex/
 * 298 :named

Running analysis...

Then you can cleanup unused definitions and keep tests codebase clean without constant supervision.

Protip: Add this command to your CI and you'll never have to worry about unused Behat definitions again. "What command?" you ask.

Add Behastan to your Project

composer require rector/behastan --dev

To run static analysis on your Behat definitions, just provide a directory with your Behat PHP definitions and feature files:

vendor/bin/behastan analyse tests

That's it!


Happy coding!