From 9c158c931bab9319a2c120a002f4bf3cb96b2f47 Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Sun, 21 Jun 2026 20:57:54 +0100 Subject: [PATCH 1/7] docs: Document our backwards compatibility policy Explain what is (and is not) guaranteed to be backwards compatible between minor and patch releases. --- releases.rst | 11 ++- releases/backwards-compatibility.rst | 101 +++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 releases/backwards-compatibility.rst diff --git a/releases.rst b/releases.rst index b837935..0702786 100644 --- a/releases.rst +++ b/releases.rst @@ -1,7 +1,9 @@ Releases & version support ========================== -Behat follows `Semantic Versioning`_ - breaking changes will only be made in a major release. +Behat follows `Semantic Versioning`_ - therefore we will only make breaking changes to the Public API in a major +release. Note that a significant portion of Behat's codebase is **not considered part of the Public API** - you +can read more in :doc:`the Backward Compatibility documentation `. Supported versions ------------------ @@ -82,6 +84,13 @@ Major Released Bugfix EOL Security EOL `v2.x`_ July 2011 June 2015 June 2015 `Changelog `__ ======= ========== ============ ============ ===================================================================== +Additional Releases documentation +--------------------------------- + +.. toctree:: + :maxdepth: 1 + + releases/backwards-compatibility .. _`Semantic Versioning`: http://semver.org/ .. _`official php.net version support page`: https://www.php.net/supported-versions.php diff --git a/releases/backwards-compatibility.rst b/releases/backwards-compatibility.rst new file mode 100644 index 0000000..8db6cc3 --- /dev/null +++ b/releases/backwards-compatibility.rst @@ -0,0 +1,101 @@ +Backwards Compatibility and the Behat Public API +================================================ + +Behat follows Semantic Versioning - so for minor or patch releases we promise that: + +* CLI arguments and options will not change meaning or be removed. New arguments + and options may be added. +* Configuration options will not change meaning or be removed. New options + may be added - their default value will match existing behaviour. +* Fields in machine-readable output formats (e.g. JSON or JUnit) will not change + meaning or data type or be removed. New fields may be added, and the formatting + of text fields (for example, whitespace or line breaks within descriptions) + may change. +* Any change to the interfaces, classes, and behaviour of the public API will + be backwards-compatible with the previous version. +* Behat will run end-user code (step definitions, hooks, transformations, etc) + consistently (including the sequence of hooks etc) between versions. +* Behat's event dispatcher will fire the same events, in the same sequence, + with the same properties. New events may be added before, after or between + existing events. + +The only exception to this promise is if the existing implementation contains +a bug or security issue that cannot be fixed without a behaviour change. + +Note that from 4.0 onwards we do **not guarantee** the exact format or +structure of human-readable output formats (e.g. progress or pretty). + +What counts as Behat's "Public API"? +------------------------------------ + +Since v3.30.0, all interfaces, classes and methods within the Behat codebase +are **internal by default**. We follow the +`phpstan backwards compatibility convention`_ that code is only part of our +public API if we have explicitly tagged it as ``@api``. + +* In Behat >= 4.0, the backwards compatibility promise only covers code that + meets the rules summarised below. Anything that is not tagged ``@api`` is + subject to change in any major, minor, or patch release. +* In Behat 3.x, we will maintain backwards compatibility for the whole + codebase - if you are using non-``@api`` code, we recommend either + updating your integration or requesting it be published. + +.. note:: + + We are happy to consider requests to add ``@api`` tags to more of the + Behat codebase. These will be assessed based on the usecase, whether it + is possible to achieve the same thing in a different way, and the + potential impact on ongoing maintenance and development of Behat. + + Please open an issue (or better yet a PR) with details of what you'd + like to be published, how you will use it, and any alternatives + you've considered. + +Classes +~~~~~~~ + +* Only (non-final) classes with ``@api`` in their PHPDoc can be extended. +* Objects can only be created via ``new`` if the PHPDoc of the + public constructor is directly tagged with ``@api``. In all other cases + (even if the class PHPDoc has ``@api``), instances of the class should + only be created by / retrieved from Behat's service container. +* Methods can only be called if there is an ``@api`` tag in the PHPDoc + of the method, declaring class, or declaring interface. +* Constants can only be accessed if there is an ``@api`` tag in the + PHPDoc of the constant, declaring class, or declaring interface. + +Interfaces +~~~~~~~~~~ + +* Interfaces with ``@api`` in their PHPDoc can be implemented and extended. +* All methods from interfaces with ``@api`` in their PHPDoc can be called. + +Traits +~~~~~~ + +* No Behat traits can be used. + + +Other considerations for extension authors +------------------------------------------ + +Bear in mind that the backwards compatibility promise only covers removing +or changing existing code. We may still add new CLI arguments, new config +options, new classes, new methods, etc. + +Therefore when writing code to extend or integrate with Behat: + +* Do not place your own code in any ``Behat`` namespace. +* Do not add new configuration options outside your extension's own + config namespace (the node passed to your extension's ``::configure`` + method). +* You *can* add new CLI commands, flags and arguments - but we recommend + avoiding this wherever possible due to the risk of naming conflicts + with future Behat versions or other extensions. Prefer using config + files and e.g. Behat's ``--profile`` feature to switch between modes + from the CLI. +* If you are extending a Behat class (with an ``@api`` tag), avoid + adding new methods where possible. This will protect you against + naming conflicts with methods that may be added to Behat in future. + +.. _`phpstan backwards compatibility convention`: https://phpstan.org/developing-extensions/backward-compatibility-promise From ffec67b62cf3308217f91a47b7c02087732720cd Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Sun, 21 Jun 2026 23:45:57 +0100 Subject: [PATCH 2/7] docs: Add draft 4.0 upgrade guide --- releases.rst | 10 +-- releases/upgrading-to-4.0.rst | 120 ++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 releases/upgrading-to-4.0.rst diff --git a/releases.rst b/releases.rst index 0702786..c9da00a 100644 --- a/releases.rst +++ b/releases.rst @@ -8,12 +8,12 @@ can read more in :doc:`the Backward Compatibility documentation `__ -`v4.x`_ tbc 2025/6 See below See below `Changelog `__ -======= ========== ========== ============ ======================================================================= +`v4.x`_ tbc Q3/26 See below See below `4.x Changelog`_ :doc:`Upgrading ` +======= ========== ========== ============ ==================================================================== As a minimum, a major version series will receive: @@ -91,6 +91,7 @@ Additional Releases documentation :maxdepth: 1 releases/backwards-compatibility + releases/upgrading-to-4.0 .. _`Semantic Versioning`: http://semver.org/ .. _`official php.net version support page`: https://www.php.net/supported-versions.php @@ -98,3 +99,4 @@ Additional Releases documentation .. _`v2.x`: https://github.com/Behat/Behat/releases?q=v2 .. _`v3.x`: https://github.com/Behat/Behat/releases?q=v3 .. _`v4.x`: https://github.com/Behat/Behat/releases?q=v4 +.. _`4.x Changelog`: https://github.com/Behat/Behat/blob/4.x/CHANGELOG.md diff --git a/releases/upgrading-to-4.0.rst b/releases/upgrading-to-4.0.rst new file mode 100644 index 0000000..6133068 --- /dev/null +++ b/releases/upgrading-to-4.0.rst @@ -0,0 +1,120 @@ +Upgrading to Behat 4.0 +====================== + +We have tried to make it as easy as possible to upgrade to Behat 4.0, but it does +contain some breaking changes. + +If you are an end-user (you use Behat to run your specifications, but you haven't +extended / integrated with the Behat internals), then many of these will not affect +you. We also expect that you will be able to resolve the majority of these with +automated tools - follow this guide to find out more. + +Upgrading for end-users +----------------------- + +For end-user projects, the major changes are: + +* We no longer support YAML configuration - and we only automatically detect + config from ``behat.php`` or ``behat.dist.php`` in the directory where you + run Behat. +* We no longer support PHPDoc annotations to mark up step definitions, hooks and + argument transformations. +* All deprecated features and code have been removed. +* We now default to the :doc:`GHERKIN_32 parser compatibility mode `. + +Preparing to upgrade +~~~~~~~~~~~~~~~~~~~~ + +**Before** you attempt to update to Behat 4.0, you should make all of these changes: + +1. Ensure you are running the latest Behat 3.x +2. If you have YAML configuration, run ``vendor/bin/behat --convert-config`` to + `convert it to the new PHP config`_. Review the output carefully - the tool will handle most + common configurations but is not guaranteed to cater for all unusual or complex cases. +3. If your Behat config file is in a ``config/`` subdirectory, either move it to the directory + where you run Behat, or add the ``--config {PATH TO FILE}`` argument to any Behat runs. +4. If you use any extensions, check that these are enabled using the fully-qualified class name + of the ``Extension`` class. The ``--convert-config`` tool will usually do this for you. +5. Run `Rector`_ with the ``->withAttributeSets(behat: true)`` rule to convert any + `Behat annotations`_ into PHP Attributes. If your project isn't already using Rector, you + can install it temporarily and just run that rule. +6. Run your full Behat suite(s) with the ``--fail-on-deprecations`` option and fix any failures. +7. Configure Behat to use the :doc:`GHERKIN_32 parser compatibility mode ` + and check if this affects your project. If this causes issues, you can either fix your feature + files or update your Behat configuration to force the ``GherkinCompatibilityMode::LEGACY``. + +Upgrading +~~~~~~~~~ + +If you have followed the steps above, you should be ready to update your composer.json and +start using Behat 4.0! + +.. caution:: + We expect there will be a small number of breaking changes between 4.0.0-alpha1 and + 4.0.0. We recommend ``{"require": {"behat/behat": "4.0.0-alpha1@alpha"}}`` for now. + See below for more details. + +.. note:: + If you use any third-party Behat extensions, you will need to update these to a version + that supports Behat 4.0. If you find an extension that hasn't yet been updated, please + consider submitting a PR. The more the Behat community contributes to updating the + ecosystem, the quicker we will all get there :) + + +Upgrading for extension authors +------------------------------- + +It should be possible to support Behat 3.x and 4.x simultaneously (e.g. +``{"require": {"behat/behat": "^3.x || ^4.x"}}``). + +If your project uses Behat directly (e.g. to "dog-food" your extension / run your own features) +you will first need to follow the steps above for upgrading end-user projects. + +The major changes for all extension authors are: + +* All interfaces and classes now have strict parameter, property & return types. You will + need to add return types to all methods that implement Behat interfaces or extend Behat classes. + ``Rector`` can do this for you with the ``AddReturnTypeBasedOnParentClassMethodRector``. + This is included by default if you enable Rector's ``typeDeclarations`` set. +* We are now much stricter about what counts as the public API (and will therefore be covered + by the :doc:`backwards compatibility promise ` in future). + If your extension needs to use Behat code that we haven't marked as public, please let us know. +* We no longer support users referencing an extension by a "short name" (e.g. ``Behat\MinkExtension``). + Users must always give the fully-qualified name of your ``Extension`` class. Please update your + documentation. +* If your extension needs to report deprecations, we recommend calling + ``Behat\Testwork\Deprecation\DeprecationCollector::trigger()`` (available since 3.30.0) instead + of the native ``trigger_error``. This will guarantee it is handled by the ``--print-deprecations`` + / ``--fail-on-deprecations`` options in all cases. +* The ``ScenarioLikeTested`` base event class no longer exists. ``ScenarioTested`` and + ``BackgroundTested`` are now separate event families. This is particularly likely to affect + formatter extensions. + +There are several other changes that might affect a minority of extension authors. See the full +`CHANGELOG`_ for details. + + +Planned changes between 4.0.0-alpha1 and 4.0.0 +---------------------------------------------- + +There are two significant changes that we plan to make before 4.0.0: + +* We will review the behaviour of steps where the number of function parameters does not match + the number of parameters in the step definition text. As a minimum, this will trigger a + deprecation - it may trigger a failure. See `#1691`_. +* Support for rendering details of a PHPUnit assertion failure will move out of Behat core + into a standalone extension. Without the extension, tests will still pass/fail as expected + but the failure output will be less useful. See `#1746`_. This is because PHPUnit assertions + are not designed to be used outside PHPUnit and we no longer recommend using PHPUnit for + assertions within Behat steps. + +We may make more changes before 4.0, depending on feedback from extension authors as they start +to upgrade. + +.. _`convert it to the new PHP config`: https://docs.behat.org/en/3.x/user_guide/configuration/yaml_configuration.html#converting-your-configuration +.. _`Rector`: https://getrector.com/documentation +.. _`Behat annotations`: https://docs.behat.org/en/v3.x/user_guide/annotations.html#existing-code +.. _`AddReturnTypeBasedOnParentClassMethodRector`: https://getrector.com/rule-detail/add-return-type-declaration-based-on-parent-class-method-rector +.. _`CHANGELOG`: https://github.com/Behat/Behat/blob/4.x/CHANGELOG.md +.. _`#1691`: https://github.com/Behat/Behat/issues/1691 +.. _`#1746`: https://github.com/Behat/Behat/issues/1746 From 2d4e409b7fbcd7dd119abd0d32f7cc3a0a4dc10e Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Mon, 22 Jun 2026 09:22:09 +0100 Subject: [PATCH 3/7] docs: Update config docs for 4.0 Remove references to YAML config and update Extensions example to show referencing the extension by its fully-qualified class name. --- cookbooks/custom_formatter.rst | 5 +- user_guide/configuration.rst | 29 ++++---- user_guide/configuration/suites.rst | 8 +-- .../configuration/yaml_configuration.rst | 66 ------------------- user_guide/initialize.rst | 2 +- 5 files changed, 20 insertions(+), 90 deletions(-) delete mode 100644 user_guide/configuration/yaml_configuration.rst diff --git a/cookbooks/custom_formatter.rst b/cookbooks/custom_formatter.rst index 578f598..31668a9 100644 --- a/cookbooks/custom_formatter.rst +++ b/cookbooks/custom_formatter.rst @@ -150,7 +150,7 @@ etc. } /** - * setParameter will be called for each key given to the formatter in your behat.yml file. + * setParameter will be called for each key given to the formatter in your behat.php file. * We will see that later in the "integration". * In our case, the only allowed parameter is a "file_name" that must be a string : the JSON file that we will write. */ @@ -194,7 +194,7 @@ etc. } /** - * This is the name of the formatter, that will be used in the behat.yml file + * This is the name of the formatter, that will be used in the behat.php file */ public function getName(): string { @@ -508,4 +508,3 @@ About the author Written by `Julien Deniau `__, originally posted as a blog post `on his blog `__. - diff --git a/user_guide/configuration.rst b/user_guide/configuration.rst index b5db886..0873aef 100644 --- a/user_guide/configuration.rst +++ b/user_guide/configuration.rst @@ -9,24 +9,15 @@ profiles. configuration/suites.rst configuration/printing_paths.rst - configuration/yaml_configuration.rst ``behat.php`` ------------- -All configuration happens inside a single configuration file in the ``PHP`` or the ``YAML`` -format. By default, Behat loads the configuration from the first file matching: +All configuration happens inside a single PHP configuration file. By default, Behat loads +the configuration from your current working directory from the first file matching: -#. ``behat.yaml`` or ``behat.yml`` -#. ``behat.yaml.dist`` or ``behat.yml.dist`` -#. ``behat.dist.yaml`` or ``behat.dist.yml`` #. ``behat.php`` #. ``behat.dist.php`` -#. ``config/behat.yaml`` or ``config/behat.yml`` -#. ``config/behat.yaml.dist`` or ``config/behat.yml.dist`` -#. ``config/behat.dist.yaml`` or ``config/behat.dist.yml`` -#. ``config/behat.php`` -#. ``config/behat.dist.php`` You can also tell Behat where your config file is with the ``--config`` option: @@ -34,10 +25,15 @@ You can also tell Behat where your config file is with the ``--config`` option: $ behat --config custom-config.php -All configuration parameters in that file are defined under a profile name root -(``default:`` for example). A profile is just a custom name you can use to -quickly switch testing configuration by using the ``--profile`` option when -executing your feature suite. +.. note:: + + From Behat 4.0, we no longer support the old YAML config format. You can + automatically convert this to PHP before you upgrade - see + :doc:`the 4.0 upgrade guide `. + +All configuration parameters in that file are defined under a profile. A profile is just +a custom name you can use to quickly switch testing configuration by using the +``--profile`` option when executing your feature suite. The default profile is always ``default``. All other profiles inherit parameters from the ``default`` profile. If you only need one profile, define @@ -428,12 +424,13 @@ Extensions can be configured like this: use Behat\Config\Config; use Behat\Config\Profile; use Behat\Config\Formatter\PrettyFormatter; + use Behat\MinkExtension\ServiceContainer\MinkExtension; return new Config() ->withProfile( new Profile('default') >withExtension( - new Extension('Behat\MinkExtension', [ + new Extension(MinkExtension::class, [ 'base_url' => 'http://www.example.com', 'selenium2' => null, ]) diff --git a/user_guide/configuration/suites.rst b/user_guide/configuration/suites.rst index 724044f..00aa704 100644 --- a/user_guide/configuration/suites.rst +++ b/user_guide/configuration/suites.rst @@ -8,7 +8,7 @@ concrete features together with the information on how to test them. With suites you can configure Behat to test different kinds of features using different kinds of contexts and doing so in one run. Test suites are -really powerful and ``behat.yml`` makes them that much more powerful: +really powerful and ``behat.php`` makes them that much more powerful: .. code-block:: php @@ -161,9 +161,9 @@ This will cause Behat to: .. note:: - ``%paths.base%`` is a special variable in ``behat.yml`` that refers - to the folder in which ``behat.yml`` is stored. When using it, or - any other percent-encased variable, it has to be put in quotes. + ``%paths.base%`` is a special placeholder in Behat configuration that + refers to the directory containing the currently active config file. + By default this will be the working directory where you are running Behat. Path-based suites are an easy way to test highly-modular applications where features are delivered by highly decoupled components. With suites diff --git a/user_guide/configuration/yaml_configuration.rst b/user_guide/configuration/yaml_configuration.rst deleted file mode 100644 index 6cb6e9e..0000000 --- a/user_guide/configuration/yaml_configuration.rst +++ /dev/null @@ -1,66 +0,0 @@ -Yaml configuration -================== - -Currently the preferred way to define the configuration for your Behat project -is by using one or more files with PHP configuration. - -But historically this configuration could also be expressed in files in the Yaml -format. This possibility is still available for now, and you can use it instead -of PHP configuration in your projects - however it will likely be deprecated and -removed in the future. - -Here is an example of how some configuration could look using a Yaml file: - -.. code-block:: yaml - - imports: - - imported.yaml - - default: - formatters: - progress: ~ - junit: false - suites: - my_suite: - contexts: - - MyContext - paths: - - "one.feature" - filters: - tags: "@run" - extensions: - MyCustomExtension: ~ - -Converting your configuration ------------------------------ - -If your project uses the legacy Yaml format for your configuration, you can easily -convert them to the PHP format by using the built in config converter. - -To use it, just run: - -.. code-block:: bash - - vendor/bin/behat --convert-config - -This will load the default config file loaded by your project and convert it from the -Yaml format to the PHP format. It will also convert any config files that are imported -by this main file. It will then remove the old Yaml files. - -We recommend carefully reviewing the generated config, particularly if you are using extensions, -custom formatters, or have complex configuration files. - -If you want to convert any other config file which is not your default config file (for -example a config file used in the CI environment), just load it with the ``-c`` -(or ``--config``) option like this: - -.. code-block:: bash - - vendor/bin/behat --convert-config -c ci-behat.yml - -This will load the ``ci-behat.yml`` config file and convert it to the PHP format. - -.. note:: - - Behat needs to be able to load this config file before converting it, so it must be - a valid Behat config file. diff --git a/user_guide/initialize.rst b/user_guide/initialize.rst index 4552c6e..034dc24 100644 --- a/user_guide/initialize.rst +++ b/user_guide/initialize.rst @@ -45,5 +45,5 @@ Suite Initialisation Suites are a core part of Behat. Any feature of Behat knows about them and can give you a hand with them. For example, if you defined -your suites in ``behat.yml`` before running ``--init``, it will actually +your suites in ``behat.php`` before running ``--init``, it will actually create the folders and suites you configured, instead of the default ones. From 4603ed575a7bd59259f6d890a9b6acd975b3e5e7 Mon Sep 17 00:00:00 2001 From: acoulton Date: Mon, 22 Jun 2026 10:21:51 +0100 Subject: [PATCH 4/7] docs: Remove references to PHPDoc annotations --- guides.rst | 1 - quick_start.rst | 9 ++- user_guide/annotations.rst | 118 ------------------------------------- 3 files changed, 4 insertions(+), 124 deletions(-) delete mode 100644 user_guide/annotations.rst diff --git a/guides.rst b/guides.rst index 12e3f9d..a68d3b6 100755 --- a/guides.rst +++ b/guides.rst @@ -75,7 +75,6 @@ Documentation Contents user_guide/writing_scenarios user_guide/organizing user_guide/context - user_guide/annotations user_guide/command_line_tool user_guide/configuration user_guide/integrations diff --git a/quick_start.rst b/quick_start.rst index aad9bb7..a881cdc 100644 --- a/quick_start.rst +++ b/quick_start.rst @@ -282,11 +282,10 @@ in our case) and tell Behat that this code represents a specific scenario step .. note:: - Behat uses PHP Attributes for step definitions, step - transformations and hooks. It also supports doc-block - annotations for compatibility with legacy code, but this - syntax is deprecated - see the :doc:`annotations ` documentation - for details. + Behat uses PHP Attributes for step definitions, step transformations and hooks. + In Behat 4.0, we removed support for defining this with PHPDoc annotations. + You can automatically convert these to attributes - see + :doc:`the 4.0 upgrade guide `. ``#[Given('there is a(n) :arg1, which costs £:arg2')]`` above the method tells Behat that this particular method should be executed whenever Behat sees step that diff --git a/user_guide/annotations.rst b/user_guide/annotations.rst deleted file mode 100644 index 2abc910..0000000 --- a/user_guide/annotations.rst +++ /dev/null @@ -1,118 +0,0 @@ -Annotations -=========== - -Historically Behat used doc-block annotations instead of attributes to define steps, hooks and -transformations in PHP contexts. These annotations are still available for now, and you can use them instead -of PHP attributes in your projects - however they will likely be deprecated and removed in the future. - -Step Annotations ----------------- - -Here is an example of how you can define your steps using annotations: - -.. code-block:: php - - // features/bootstrap/FeatureContext.php - - use Behat\Behat\Context\Context; - - class FeatureContext implements Context - { - /* - * @Given we have some context - */ - public function prepareContext() - { - // do something - } - - /* - * @When an :event occurs - */ - public function onEvent(string $event) - { - // do something - } - - /* - * @Then something should be done - */ - public function checkOutcomes() - { - // do something - } - } - -The pattern that you would include as an argument to the step attribute should be listed here -after the annotation, separated by a space - -Hook Annotations ----------------- - -Here is an example of how you can define your hooks using annotations: - -.. code-block:: php - - // features/bootstrap/FeatureContext.php - - use Behat\Behat\Context\Context; - use Behat\Testwork\Hook\Scope\BeforeSuiteScope; - use Behat\Behat\Hook\Scope\AfterScenarioScope; - - class FeatureContext implements Context - { - /* - * @BeforeSuite - */ - public static function prepare(BeforeSuiteScope $scope) - { - } - - /* - * @AfterScenario @database - */ - public function cleanDB(AfterScenarioScope $scope) - { - } - } - -Transformation Annotations --------------------------- - -Here is an example of how you can define your transformations using annotations: - -.. code-block:: php - - // features/bootstrap/FeatureContext.php - - use Behat\Behat\Context\Context; - - class FeatureContext implements Context - { - /* - * @Transform /^(\d+)$/ - */ - public function castStringToNumber($string) - { - return intval($string); - } - } - -Existing code -------------- - -Even though annotations are still available, they will probably be deprecated and eventually removed in the future. -Therefore, we do not recommend using annotations for new projects. If your current project uses annotations, we -recommend that you refactor your code to use PHP attributes instead. This can be done manually but you should be able -to use the search and replace capabilities of your IDE to do this in a more automated way. - -Alternatively you may want to use a tool like `Rector`_ which can do automated refactoring of your code. Rector 2.0 -includes a rule that allows you to automatically convert any doc-block annotations to the corresponding attributes. -To use it for your Behat contexts, add the following option to your Rector configuration: - -.. code-block:: php - - ->withAttributesSets(behat: true) - -.. _`Rector`: https://github.com/rectorphp/rector - From 94666ad2a637fba84ef930b00a84fd08ee1d1728 Mon Sep 17 00:00:00 2001 From: acoulton Date: Mon, 22 Jun 2026 18:06:36 +0100 Subject: [PATCH 5/7] docs: Copy edits Simplify some wording, make it friendlier, improve clarity of the lists of actions. --- releases/backwards-compatibility.rst | 98 +++++++--------- releases/upgrading-to-4.0.rst | 169 +++++++++++++-------------- 2 files changed, 118 insertions(+), 149 deletions(-) diff --git a/releases/backwards-compatibility.rst b/releases/backwards-compatibility.rst index 8db6cc3..e4e650b 100644 --- a/releases/backwards-compatibility.rst +++ b/releases/backwards-compatibility.rst @@ -1,55 +1,44 @@ Backwards Compatibility and the Behat Public API ================================================ -Behat follows Semantic Versioning - so for minor or patch releases we promise that: - -* CLI arguments and options will not change meaning or be removed. New arguments - and options may be added. -* Configuration options will not change meaning or be removed. New options - may be added - their default value will match existing behaviour. -* Fields in machine-readable output formats (e.g. JSON or JUnit) will not change - meaning or data type or be removed. New fields may be added, and the formatting - of text fields (for example, whitespace or line breaks within descriptions) - may change. -* Any change to the interfaces, classes, and behaviour of the public API will - be backwards-compatible with the previous version. -* Behat will run end-user code (step definitions, hooks, transformations, etc) - consistently (including the sequence of hooks etc) between versions. -* Behat's event dispatcher will fire the same events, in the same sequence, - with the same properties. New events may be added before, after or between - existing events. - -The only exception to this promise is if the existing implementation contains -a bug or security issue that cannot be fixed without a behaviour change. - -Note that from 4.0 onwards we do **not guarantee** the exact format or -structure of human-readable output formats (e.g. progress or pretty). +Behat follows Semantic Versioning. For minor or patch releases, we aim to keep things stable: + +* CLI arguments and options will stay the same. We may add new ones, but existing ones will continue to work as they do + now. +* Configuration options will not change or be removed. New options may be added, and their default values will match + the previous behavior. +* Machine-readable output (like JSON or JUnit) will keep the same fields and data types. We might add new fields or + change how text is formatted (like adding or removing whitespace), but the structure will remain compatible. +* Any changes to the public API (interfaces, classes, and behavior) will be backwards-compatible with the previous + version. +* Your code (step definitions, hooks, transformations) will run the same way between versions - including + that we will trigger hooks in the same order. +* Behat's event dispatcher will fire the same events in the same order. We might add new events in between, but + existing ones will stay. + +The only time we might break this is to fix a critical bug or a security issue that cannot be resolved any other way. + +Note that from version 4.0 onwards, we do not guarantee the exact format of human-readable output (like the ``progress`` +or ``pretty`` formatters). These are intended for people to read, so we may improve them over time. What counts as Behat's "Public API"? ------------------------------------ -Since v3.30.0, all interfaces, classes and methods within the Behat codebase -are **internal by default**. We follow the -`phpstan backwards compatibility convention`_ that code is only part of our -public API if we have explicitly tagged it as ``@api``. +Starting with version 3.30.0, all interfaces, classes, and methods in Behat are **internal by default**. We follow the +`phpstan backwards compatibility convention`_, which means code is only part of our public API if it is explicitly +marked with the ``@api`` tag. -* In Behat >= 4.0, the backwards compatibility promise only covers code that - meets the rules summarised below. Anything that is not tagged ``@api`` is - subject to change in any major, minor, or patch release. -* In Behat 3.x, we will maintain backwards compatibility for the whole - codebase - if you are using non-``@api`` code, we recommend either - updating your integration or requesting it be published. +* In Behat 4.0 and later, our compatibility promise only applies to code marked with ``@api``. Anything else may change + in any release. +* In Behat 3.x, we maintain compatibility for the entire codebase. However, if you are using code that is not + marked ``@api``, we recommend updating your code or asking us to make it public. .. note:: - We are happy to consider requests to add ``@api`` tags to more of the - Behat codebase. These will be assessed based on the usecase, whether it - is possible to achieve the same thing in a different way, and the - potential impact on ongoing maintenance and development of Behat. + We are happy to consider making more of the Behat codebase public. If you need an ``@api`` tag added, please let + us know! We will look at your use case and see how it affects the long-term maintenance of Behat. - Please open an issue (or better yet a PR) with details of what you'd - like to be published, how you will use it, and any alternatives - you've considered. + Please open an issue (or even better a Pull Request) with details on what you would like to use and why. Classes ~~~~~~~ @@ -76,26 +65,19 @@ Traits * No Behat traits can be used. -Other considerations for extension authors ------------------------------------------- +Other tips for extension authors +------------------------------- -Bear in mind that the backwards compatibility promise only covers removing -or changing existing code. We may still add new CLI arguments, new config -options, new classes, new methods, etc. +Our compatibility promise covers changes to existing code, but we will still add new features like CLI arguments, +config options, or methods. -Therefore when writing code to extend or integrate with Behat: +To avoid conflicts with future versions of Behat, we recommend following these practices: -* Do not place your own code in any ``Behat`` namespace. -* Do not add new configuration options outside your extension's own - config namespace (the node passed to your extension's ``::configure`` - method). -* You *can* add new CLI commands, flags and arguments - but we recommend - avoiding this wherever possible due to the risk of naming conflicts - with future Behat versions or other extensions. Prefer using config - files and e.g. Behat's ``--profile`` feature to switch between modes - from the CLI. -* If you are extending a Behat class (with an ``@api`` tag), avoid - adding new methods where possible. This will protect you against - naming conflicts with methods that may be added to Behat in future. +* Do not use the ``Behat`` namespace for your own code. +* Only add configuration options within your extension's own namespace. +* While you can add new CLI commands or flags, it's safer to use configuration files or Behat's ``--profile`` feature. + This avoids naming conflicts with future Behat versions. +* If you extend a Behat class (marked ``@api``), try to avoid adding new public methods. This protects you if Behat adds + a method with the same name in the future. .. _`phpstan backwards compatibility convention`: https://phpstan.org/developing-extensions/backward-compatibility-promise diff --git a/releases/upgrading-to-4.0.rst b/releases/upgrading-to-4.0.rst index 6133068..ba083e4 100644 --- a/releases/upgrading-to-4.0.rst +++ b/releases/upgrading-to-4.0.rst @@ -1,117 +1,104 @@ Upgrading to Behat 4.0 ====================== -We have tried to make it as easy as possible to upgrade to Behat 4.0, but it does -contain some breaking changes. - -If you are an end-user (you use Behat to run your specifications, but you haven't -extended / integrated with the Behat internals), then many of these will not affect -you. We also expect that you will be able to resolve the majority of these with -automated tools - follow this guide to find out more. - -Upgrading for end-users ------------------------ - -For end-user projects, the major changes are: - -* We no longer support YAML configuration - and we only automatically detect - config from ``behat.php`` or ``behat.dist.php`` in the directory where you - run Behat. -* We no longer support PHPDoc annotations to mark up step definitions, hooks and - argument transformations. -* All deprecated features and code have been removed. -* We now default to the :doc:`GHERKIN_32 parser compatibility mode `. - -Preparing to upgrade -~~~~~~~~~~~~~~~~~~~~ - -**Before** you attempt to update to Behat 4.0, you should make all of these changes: - -1. Ensure you are running the latest Behat 3.x -2. If you have YAML configuration, run ``vendor/bin/behat --convert-config`` to - `convert it to the new PHP config`_. Review the output carefully - the tool will handle most - common configurations but is not guaranteed to cater for all unusual or complex cases. -3. If your Behat config file is in a ``config/`` subdirectory, either move it to the directory - where you run Behat, or add the ``--config {PATH TO FILE}`` argument to any Behat runs. -4. If you use any extensions, check that these are enabled using the fully-qualified class name - of the ``Extension`` class. The ``--convert-config`` tool will usually do this for you. -5. Run `Rector`_ with the ``->withAttributeSets(behat: true)`` rule to convert any - `Behat annotations`_ into PHP Attributes. If your project isn't already using Rector, you - can install it temporarily and just run that rule. -6. Run your full Behat suite(s) with the ``--fail-on-deprecations`` option and fix any failures. -7. Configure Behat to use the :doc:`GHERKIN_32 parser compatibility mode ` - and check if this affects your project. If this causes issues, you can either fix your feature - files or update your Behat configuration to force the ``GherkinCompatibilityMode::LEGACY``. - -Upgrading -~~~~~~~~~ - -If you have followed the steps above, you should be ready to update your composer.json and -start using Behat 4.0! +We want to make upgrading to Behat 4.0 as smooth as possible. While there are some breaking changes, we've tried to +keep them to a minimum. + +If you are an end-user (meaning you use Behat to run tests but haven't written custom extensions), most of these changes +won't affect you. In many cases, you can use automated tools to handle the upgrade for you. Follow this guide to get +started. + +Upgrading for users +-------------------- + +For most projects, these are the main changes: + +* **PHP Configuration:** We now use PHP for configuration. YAML files are no longer supported. Behat will look for + ``behat.php`` or ``behat.dist.php`` in your current directory. +* **PHP Attributes:** We've replaced PHPDoc annotations (like ``@Given`` or ``@BeforeScenario``) with native PHP + Attributes. +* **Cleanup:** All previously deprecated features and code have been removed. +* **New Parser Mode:** We now default to a newer parser compatibility mode + (:doc:`GHERKIN_32 `). + +Step-by-step preparation +~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Before** you switch to Behat 4.0, we recommend taking these steps: + +1. **Update Behat 3:** Make sure you are using the latest version of Behat 3.x. +2. **Convert Config:** If you use YAML, run ``vendor/bin/behat --convert-config`` to `convert it to PHP`_. Review the + results to make sure everything looks correct. +3. **Check File Location:** If your config file is in a ``config/`` folder, move it to your project root or use the + ``--config`` flag when running Behat. +4. **Update Extensions:** Make sure any extensions you use are referenced by their full class name (e.g. + ``Behat\MinkExtension\ServiceContainer\MinkExtension::class`` not ``Behat\MinkExtension```). The conversion tool + usually handles this. +5. **Convert Annotations:** Use `Rector`_ with the ``->withAttributeSets(behat: true)`` rule to automatically change + `Behat annotations`_ into PHP Attributes. +6. **Check Deprecations:** Run your tests with ``--fail-on-deprecations`` and fix any warnings that appear. +7. **Test the New Parser:** Enable the :doc:`GHERKIN_32 parser mode ` and see if your + tests still run correctly. If you have issues, you can fix your feature files or use + ``GherkinCompatibilityMode::LEGACY`` in your config. This mode will be removed in future. + +Ready to upgrade? +~~~~~~~~~~~~~~~~~ + +Once you've completed the steps above, update your ``composer.json`` to start using Behat 4.0! .. caution:: - We expect there will be a small number of breaking changes between 4.0.0-alpha1 and - 4.0.0. We recommend ``{"require": {"behat/behat": "4.0.0-alpha1@alpha"}}`` for now. - See below for more details. + While we are in the alpha phase, we recommend using: + ``{"require": {"behat/behat": "4.0.0-alpha1@alpha"}}``. .. note:: - If you use any third-party Behat extensions, you will need to update these to a version - that supports Behat 4.0. If you find an extension that hasn't yet been updated, please - consider submitting a PR. The more the Behat community contributes to updating the - ecosystem, the quicker we will all get there :) + Don't forget to update your third-party extensions to versions that support Behat 4.0. If you find one that hasn't + been updated yet, consider helping out by submitting a Pull Request! The community's help makes the transition faster + for everyone. Upgrading for extension authors ------------------------------- -It should be possible to support Behat 3.x and 4.x simultaneously (e.g. +It's possible to support both Behat 3.x and 4.x at the same time (for example, by using ``{"require": {"behat/behat": "^3.x || ^4.x"}}``). -If your project uses Behat directly (e.g. to "dog-food" your extension / run your own features) -you will first need to follow the steps above for upgrading end-user projects. - -The major changes for all extension authors are: - -* All interfaces and classes now have strict parameter, property & return types. You will - need to add return types to all methods that implement Behat interfaces or extend Behat classes. - ``Rector`` can do this for you with the ``AddReturnTypeBasedOnParentClassMethodRector``. - This is included by default if you enable Rector's ``typeDeclarations`` set. -* We are now much stricter about what counts as the public API (and will therefore be covered - by the :doc:`backwards compatibility promise ` in future). - If your extension needs to use Behat code that we haven't marked as public, please let us know. -* We no longer support users referencing an extension by a "short name" (e.g. ``Behat\MinkExtension``). - Users must always give the fully-qualified name of your ``Extension`` class. Please update your - documentation. -* If your extension needs to report deprecations, we recommend calling - ``Behat\Testwork\Deprecation\DeprecationCollector::trigger()`` (available since 3.30.0) instead - of the native ``trigger_error``. This will guarantee it is handled by the ``--print-deprecations`` - / ``--fail-on-deprecations`` options in all cases. -* The ``ScenarioLikeTested`` base event class no longer exists. ``ScenarioTested`` and - ``BackgroundTested`` are now separate event families. This is particularly likely to affect - formatter extensions. +If your project uses Behat to test itself, first follow the "Upgrading for users" steps above. + +Here are the key changes for all extension authors: + +* **Strict Types:** All interfaces and classes now use strict types for parameters, properties, and return values. As a + minimum, you will need to add return types to any methods that implement Behat interfaces or extend Behat classes. + `Rector`_ can automate this for you with the ``AddReturnTypeBasedOnParentClassMethodRector`` (included in the + ``typeDeclarations`` set). +* **Public API:** We are now stricter about what is considered public API. This helps us maintain a solid + :doc:`backwards compatibility promise `. If your extension needs to use code that + isn't marked public yet, please let us know. +* **Full Class Names:** Users can no longer use "short names" for extensions (like ``Behat\MinkExtension``). They must + now use the fully-qualified class name of your ``Extension`` class. Please update your documentation to reflect this. +* **Deprecations:** If your extension needs to report deprecations, we recommend using + ``Behat\Testwork\Deprecation\DeprecationCollector::trigger()`` (available since 3.30.0) instead of ``trigger_error``. + This ensures they are correctly handled by Behat's deprecation flags regardless of the user's runtime environment. +* **Event Changes:** The ``ScenarioLikeTested`` base event class has been removed. ``ScenarioTested`` and + ``BackgroundTested`` are now separate. This may affect you if you maintain a formatter extension. There are several other changes that might affect a minority of extension authors. See the full `CHANGELOG`_ for details. - -Planned changes between 4.0.0-alpha1 and 4.0.0 +Planned changes before the final 4.0.0 release ---------------------------------------------- -There are two significant changes that we plan to make before 4.0.0: +We plan to make two more significant changes before the final 4.0.0 release: -* We will review the behaviour of steps where the number of function parameters does not match - the number of parameters in the step definition text. As a minimum, this will trigger a - deprecation - it may trigger a failure. See `#1691`_. -* Support for rendering details of a PHPUnit assertion failure will move out of Behat core - into a standalone extension. Without the extension, tests will still pass/fail as expected - but the failure output will be less useful. See `#1746`_. This is because PHPUnit assertions - are not designed to be used outside PHPUnit and we no longer recommend using PHPUnit for - assertions within Behat steps. +* **Parameter Matching:** We are reviewing how steps behave when the number of function parameters doesn't match the + step definition. This will likely trigger a deprecation or a failure. You can follow the progress in `#1691`_. +* **PHPUnit Assertions:** Support for rendering PHPUnit assertion failures will move to a standalone extension. While + tests will still pass or fail, the output will be less detailed without the extension. We no longer recommend using + PHPUnit for assertions within Behat steps, as the PHPUnit project has confirmed this is not supported. See + `#1746`_ for details. -We may make more changes before 4.0, depending on feedback from extension authors as they start -to upgrade. +We may make additional changes based on feedback from the community as more people begin to upgrade. -.. _`convert it to the new PHP config`: https://docs.behat.org/en/3.x/user_guide/configuration/yaml_configuration.html#converting-your-configuration +.. _`convert it to PHP`: https://docs.behat.org/en/v3.x/user_guide/configuration/yaml_configuration.html#converting-your-configuration .. _`Rector`: https://getrector.com/documentation .. _`Behat annotations`: https://docs.behat.org/en/v3.x/user_guide/annotations.html#existing-code .. _`AddReturnTypeBasedOnParentClassMethodRector`: https://getrector.com/rule-detail/add-return-type-declaration-based-on-parent-class-method-rector From 8d0ad574d63c19a8d324f497f3802db4148b3f2c Mon Sep 17 00:00:00 2001 From: acoulton Date: Mon, 22 Jun 2026 18:09:30 +0100 Subject: [PATCH 6/7] docs: Copy edits Simplify some wording, make it friendlier, improve clarity of the lists of actions. --- releases/backwards-compatibility.rst | 2 +- releases/upgrading-to-4.0.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/releases/backwards-compatibility.rst b/releases/backwards-compatibility.rst index e4e650b..1de3d23 100644 --- a/releases/backwards-compatibility.rst +++ b/releases/backwards-compatibility.rst @@ -66,7 +66,7 @@ Traits Other tips for extension authors -------------------------------- +-------------------------------- Our compatibility promise covers changes to existing code, but we will still add new features like CLI arguments, config options, or methods. diff --git a/releases/upgrading-to-4.0.rst b/releases/upgrading-to-4.0.rst index ba083e4..103e0db 100644 --- a/releases/upgrading-to-4.0.rst +++ b/releases/upgrading-to-4.0.rst @@ -77,7 +77,7 @@ Here are the key changes for all extension authors: now use the fully-qualified class name of your ``Extension`` class. Please update your documentation to reflect this. * **Deprecations:** If your extension needs to report deprecations, we recommend using ``Behat\Testwork\Deprecation\DeprecationCollector::trigger()`` (available since 3.30.0) instead of ``trigger_error``. - This ensures they are correctly handled by Behat's deprecation flags regardless of the user's runtime environment. + This ensures they are correctly handled by Behat's deprecation flags regardless of the user's runtime environment. * **Event Changes:** The ``ScenarioLikeTested`` base event class has been removed. ``ScenarioTested`` and ``BackgroundTested`` are now separate. This may affect you if you maintain a formatter extension. From 7d5ba376d1e8c0c7b788ed0a9ec724320a274522 Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Mon, 22 Jun 2026 22:04:28 +0100 Subject: [PATCH 7/7] fix: grammar from review --- releases/upgrading-to-4.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releases/upgrading-to-4.0.rst b/releases/upgrading-to-4.0.rst index 103e0db..1c8e22a 100644 --- a/releases/upgrading-to-4.0.rst +++ b/releases/upgrading-to-4.0.rst @@ -39,7 +39,7 @@ Step-by-step preparation 6. **Check Deprecations:** Run your tests with ``--fail-on-deprecations`` and fix any warnings that appear. 7. **Test the New Parser:** Enable the :doc:`GHERKIN_32 parser mode ` and see if your tests still run correctly. If you have issues, you can fix your feature files or use - ``GherkinCompatibilityMode::LEGACY`` in your config. This mode will be removed in future. + ``GherkinCompatibilityMode::LEGACY`` in your config. This mode will be removed in the future. Ready to upgrade? ~~~~~~~~~~~~~~~~~