Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: update docs to use Attributes instead of Annotations #178

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cookbooks/accessing_contexts_from_each_other.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ so other contexts can be retrieved using a ``BeforeScenario`` hook:

use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Hook\BeforeScenario;

class FeatureContext implements Context
{
/** @var \Behat\MinkExtension\Context\MinkContext */
private $minkContext;

/** @BeforeScenario */
#[BeforeScenario]
public function gatherContexts(BeforeScenarioScope $scope)
{
$environment = $scope->getEnvironment();
Expand Down
3 changes: 2 additions & 1 deletion cookbooks/creating_a_context_configuration_extension.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ The configuration will also control whether the behaviour is enabled or not.

use Behat\Behat\Context\Context as BehatContext;
use Behat\Behat\Tester\Exception\PendingException;
use Behat\Step\Given;

class HelloWorldContext implements BehatContext
{
Expand All @@ -45,7 +46,7 @@ The configuration will also control whether the behaviour is enabled or not.
$this->text = $text;
}

/** @Given I say Hello World */
#[Given('I say Hello World')]
public function helloWorld()
{
if ($this->enable) {
Expand Down
71 changes: 29 additions & 42 deletions quick_start.rst
Original file line number Diff line number Diff line change
Expand Up @@ -270,30 +270,31 @@ Finally, we got to the automation part. How does Behat know what to do
when it sees ``Given there is a "Sith Lord Lightsaber", which costs £5``? You
tell it. You write PHP code inside your context class (``FeatureContext``
in our case) and tell Behat that this code represents a specific scenario step
(via an annotation with a pattern):
(via an attribute with a pattern):

.. code-block:: php

/**
* @Given there is a(n) :arg1, which costs £:arg2
*/
#[Given('there is a(n) :arg1, which costs £:arg2')]
public function thereIsAWhichCostsPs($arg1, $arg2)
{
throw new PendingException();
}

.. note::

``/** ... */`` is a special syntax in PHP called a doc-block. It is
discoverable at runtime and used by different PHP frameworks as a
way to provide additional meta-information for the classes, methods and
functions. Behat uses doc-blocks for step definitions, step
transformations and hooks.
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 </user_guide/annotations>` documentation
for details.

``@Given there is a(n) :arg1, which costs £:arg2`` above the method tells Behat
``#[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
looks like ``... there is a ..., which costs £...``. This pattern will match
any of the following steps:
looks like ``... there is a ..., which costs £...``.

The ``#[Given]``, ``#[When]`` and ``#[Then]`` attributes are functionally identical - they
only exist separately to help keep the wording of your step definitions readable. So
this pattern will match any of the following steps:

.. code-block:: gherkin

Expand Down Expand Up @@ -324,9 +325,7 @@ Not only that, but Behat will capture tokens (words starting with ``:``, e.g.

.. code-block:: php

/**
* @Given /there is an? \"([^\"]+)\", which costs £([\d\.]+)/
*/
#[Given('/there is an? \"([^\"]+)\", which costs £([\d\.]+)/')]
public function thereIsAWhichCostsPs($arg1, $arg2)
{
throw new PendingException();
Expand All @@ -341,9 +340,7 @@ got:

--- FeatureContext has missing steps. Define them with these snippets:

/**
* @Given there is a :arg1, which costs £:arg2
*/
#[Given('there is a :arg1, which costs £:arg2')]
public function thereIsAWhichCostsPs($arg1, $arg2)
{
throw new PendingException();
Expand All @@ -370,36 +367,31 @@ If you executed ``--append-snippets``, your ``FeatureContext`` should look like:
use Behat\Behat\Context\SnippetAcceptingContext;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
use Behat\Step\Given;
use Behat\Step\Then;
use Behat\Step\When;

class FeatureContext implements SnippetAcceptingContext
{
/**
* @Given there is a :arg1, which costs £:arg2
*/
#[Given('there is a :arg1, which costs £:arg2')]
public function thereIsAWhichCostsPs($arg1, $arg2)
{
throw new PendingException();
}

/**
* @When I add the :arg1 to the basket
*/
#[When('I add the :arg1 to the basket')]
public function iAddTheToTheBasket($arg1)
{
throw new PendingException();
}

/**
* @Then I should have :arg1 product(s) in the basket
*/
#[Then('I should have :arg1 product(s) in the basket')]
public function iShouldHaveProductInTheBasket($arg1)
{
throw new PendingException();
}

/**
* @Then the overall basket price should be £:arg1
*/
#[Then('the overall basket price should be £:arg1')]
public function theOverallBasketPriceShouldBePs($arg1)
{
throw new PendingException();
Expand Down Expand Up @@ -433,6 +425,9 @@ code we could come up with to fulfil our scenario. Something like this:
use Behat\Behat\Context\SnippetAcceptingContext;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
use Behat\Step\Given;
use Behat\Step\Then;
use Behat\Step\When;

class FeatureContext implements SnippetAcceptingContext
{
Expand All @@ -445,25 +440,19 @@ code we could come up with to fulfil our scenario. Something like this:
$this->basket = new Basket($this->shelf);
}

/**
* @Given there is a :product, which costs £:price
*/
#[Given('there is a :arg1, which costs £:arg2')]
public function thereIsAWhichCostsPs($product, $price)
{
$this->shelf->setProductPrice($product, floatval($price));
}

/**
* @When I add the :product to the basket
*/
#[When('I add the :arg1 to the basket')]
public function iAddTheToTheBasket($product)
{
$this->basket->addProduct($product);
}

/**
* @Then I should have :count product(s) in the basket
*/
#[Then('I should have :arg1 product(s) in the basket')]
public function iShouldHaveProductInTheBasket($count)
{
// Normally you would import this class - we are using the fully qualified name
Expand All @@ -474,9 +463,7 @@ code we could come up with to fulfil our scenario. Something like this:
);
}

/**
* @Then the overall basket price should be £:price
*/
#[Then('the overall basket price should be £:arg1')]
public function theOverallBasketPriceShouldBePs($price)
{
\PHPUnit\Framework\Assert::assertSame(
Expand Down
6 changes: 4 additions & 2 deletions releases.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,17 @@ Behat only supports current versions of PHP and third-party dependency packages

By "current", we mean:

* PHP versions that are listed as receiving active support or security fixes on the `official php.net version support page`_.
* PHP versions that are listed as receiving active support or security fixes
on the `official php.net version support page`_.
* Symfony versions that are listed as maintained or receiving security fixes on the `official Symfony releases page`_.

Once a PHP or Symfony version reaches End of Life we will remove it from our composer.json and CI flows.

.. note::
When we drop support for a PHP / dependency version we will highlight this in the CHANGELOG, but we will treat
it as a minor release. Composer will automatically protect users from upgrading to a version that does not support
their environment. Users running Behat as a ``.phar`` should review the release notes before downloading a new version.
their environment. Users running Behat as a ``.phar`` should review the release notes before downloading
a new version.

We will not ship bugfix releases for unsupported PHP / dependency versions, unless:

Expand Down
1 change: 1 addition & 0 deletions user_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ User Guide
user_guide/writing_scenarios
user_guide/organizing
user_guide/context
user_guide/annotations
user_guide/command_line_tool
user_guide/configuration
user_guide/integrations
118 changes: 118 additions & 0 deletions user_guide/annotations.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
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

Loading
Loading