Skip to content

Commit 21caa65

Browse files
authored
Merge pull request #178 from carlos-granados/attributes-instead-of-annotations
chore: update docs to use Attributes instead of Annotations
2 parents 6d8c670 + 738b879 commit 21caa65

File tree

10 files changed

+274
-184
lines changed

10 files changed

+274
-184
lines changed

cookbooks/accessing_contexts_from_each_other.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ so other contexts can be retrieved using a ``BeforeScenario`` hook:
1313
1414
use Behat\Behat\Context\Context;
1515
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
16+
use Behat\Hook\BeforeScenario;
1617
1718
class FeatureContext implements Context
1819
{
1920
/** @var \Behat\MinkExtension\Context\MinkContext */
2021
private $minkContext;
2122
22-
/** @BeforeScenario */
23+
#[BeforeScenario]
2324
public function gatherContexts(BeforeScenarioScope $scope)
2425
{
2526
$environment = $scope->getEnvironment();

cookbooks/creating_a_context_configuration_extension.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ The configuration will also control whether the behaviour is enabled or not.
3030
3131
use Behat\Behat\Context\Context as BehatContext;
3232
use Behat\Behat\Tester\Exception\PendingException;
33+
use Behat\Step\Given;
3334
3435
class HelloWorldContext implements BehatContext
3536
{
@@ -45,7 +46,7 @@ The configuration will also control whether the behaviour is enabled or not.
4546
$this->text = $text;
4647
}
4748
48-
/** @Given I say Hello World */
49+
#[Given('I say Hello World')]
4950
public function helloWorld()
5051
{
5152
if ($this->enable) {

quick_start.rst

Lines changed: 29 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -270,30 +270,31 @@ Finally, we got to the automation part. How does Behat know what to do
270270
when it sees ``Given there is a "Sith Lord Lightsaber", which costs £5``? You
271271
tell it. You write PHP code inside your context class (``FeatureContext``
272272
in our case) and tell Behat that this code represents a specific scenario step
273-
(via an annotation with a pattern):
273+
(via an attribute with a pattern):
274274

275275
.. code-block:: php
276276
277-
/**
278-
* @Given there is a(n) :arg1, which costs £:arg2
279-
*/
277+
#[Given('there is a(n) :arg1, which costs £:arg2')]
280278
public function thereIsAWhichCostsPs($arg1, $arg2)
281279
{
282280
throw new PendingException();
283281
}
284282
285283
.. note::
286284

287-
``/** ... */`` is a special syntax in PHP called a doc-block. It is
288-
discoverable at runtime and used by different PHP frameworks as a
289-
way to provide additional meta-information for the classes, methods and
290-
functions. Behat uses doc-blocks for step definitions, step
291-
transformations and hooks.
285+
Behat uses PHP Attributes for step definitions, step
286+
transformations and hooks. It also supports doc-block
287+
annotations for compatibility with legacy code, but this
288+
syntax is deprecated - see the :doc:`annotations </user_guide/annotations>` documentation
289+
for details.
292290

293-
``@Given there is a(n) :arg1, which costs £:arg2`` above the method tells Behat
291+
``#[Given('there is a(n) :arg1, which costs £:arg2')]`` above the method tells Behat
294292
that this particular method should be executed whenever Behat sees step that
295-
looks like ``... there is a ..., which costs £...``. This pattern will match
296-
any of the following steps:
293+
looks like ``... there is a ..., which costs £...``.
294+
295+
The ``#[Given]``, ``#[When]`` and ``#[Then]`` attributes are functionally identical - they
296+
only exist separately to help keep the wording of your step definitions readable. So
297+
this pattern will match any of the following steps:
297298

298299
.. code-block:: gherkin
299300
@@ -324,9 +325,7 @@ Not only that, but Behat will capture tokens (words starting with ``:``, e.g.
324325

325326
.. code-block:: php
326327
327-
/**
328-
* @Given /there is an? \"([^\"]+)\", which costs £([\d\.]+)/
329-
*/
328+
#[Given('/there is an? \"([^\"]+)\", which costs £([\d\.]+)/')]
330329
public function thereIsAWhichCostsPs($arg1, $arg2)
331330
{
332331
throw new PendingException();
@@ -341,9 +340,7 @@ got:
341340
342341
--- FeatureContext has missing steps. Define them with these snippets:
343342
344-
/**
345-
* @Given there is a :arg1, which costs £:arg2
346-
*/
343+
#[Given('there is a :arg1, which costs £:arg2')]
347344
public function thereIsAWhichCostsPs($arg1, $arg2)
348345
{
349346
throw new PendingException();
@@ -370,36 +367,31 @@ If you executed ``--append-snippets``, your ``FeatureContext`` should look like:
370367
use Behat\Behat\Context\SnippetAcceptingContext;
371368
use Behat\Gherkin\Node\PyStringNode;
372369
use Behat\Gherkin\Node\TableNode;
370+
use Behat\Step\Given;
371+
use Behat\Step\Then;
372+
use Behat\Step\When;
373373
374374
class FeatureContext implements SnippetAcceptingContext
375375
{
376-
/**
377-
* @Given there is a :arg1, which costs £:arg2
378-
*/
376+
#[Given('there is a :arg1, which costs £:arg2')]
379377
public function thereIsAWhichCostsPs($arg1, $arg2)
380378
{
381379
throw new PendingException();
382380
}
383381
384-
/**
385-
* @When I add the :arg1 to the basket
386-
*/
382+
#[When('I add the :arg1 to the basket')]
387383
public function iAddTheToTheBasket($arg1)
388384
{
389385
throw new PendingException();
390386
}
391387
392-
/**
393-
* @Then I should have :arg1 product(s) in the basket
394-
*/
388+
#[Then('I should have :arg1 product(s) in the basket')]
395389
public function iShouldHaveProductInTheBasket($arg1)
396390
{
397391
throw new PendingException();
398392
}
399393
400-
/**
401-
* @Then the overall basket price should be £:arg1
402-
*/
394+
#[Then('the overall basket price should be £:arg1')]
403395
public function theOverallBasketPriceShouldBePs($arg1)
404396
{
405397
throw new PendingException();
@@ -433,6 +425,9 @@ code we could come up with to fulfil our scenario. Something like this:
433425
use Behat\Behat\Context\SnippetAcceptingContext;
434426
use Behat\Gherkin\Node\PyStringNode;
435427
use Behat\Gherkin\Node\TableNode;
428+
use Behat\Step\Given;
429+
use Behat\Step\Then;
430+
use Behat\Step\When;
436431
437432
class FeatureContext implements SnippetAcceptingContext
438433
{
@@ -445,25 +440,19 @@ code we could come up with to fulfil our scenario. Something like this:
445440
$this->basket = new Basket($this->shelf);
446441
}
447442
448-
/**
449-
* @Given there is a :product, which costs £:price
450-
*/
443+
#[Given('there is a :arg1, which costs £:arg2')]
451444
public function thereIsAWhichCostsPs($product, $price)
452445
{
453446
$this->shelf->setProductPrice($product, floatval($price));
454447
}
455448
456-
/**
457-
* @When I add the :product to the basket
458-
*/
449+
#[When('I add the :arg1 to the basket')]
459450
public function iAddTheToTheBasket($product)
460451
{
461452
$this->basket->addProduct($product);
462453
}
463454
464-
/**
465-
* @Then I should have :count product(s) in the basket
466-
*/
455+
#[Then('I should have :arg1 product(s) in the basket')]
467456
public function iShouldHaveProductInTheBasket($count)
468457
{
469458
// Normally you would import this class - we are using the fully qualified name
@@ -474,9 +463,7 @@ code we could come up with to fulfil our scenario. Something like this:
474463
);
475464
}
476465
477-
/**
478-
* @Then the overall basket price should be £:price
479-
*/
466+
#[Then('the overall basket price should be £:arg1')]
480467
public function theOverallBasketPriceShouldBePs($price)
481468
{
482469
\PHPUnit\Framework\Assert::assertSame(

releases.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,17 @@ Behat only supports current versions of PHP and third-party dependency packages
4848

4949
By "current", we mean:
5050

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

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

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

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

user_guide.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ User Guide
1010
user_guide/writing_scenarios
1111
user_guide/organizing
1212
user_guide/context
13+
user_guide/annotations
1314
user_guide/command_line_tool
1415
user_guide/configuration
1516
user_guide/integrations

user_guide/annotations.rst

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
Annotations
2+
===========
3+
4+
Historically Behat used doc-block annotations instead of attributes to define steps, hooks and
5+
transformations in PHP contexts. These annotations are still available for now, and you can use them instead
6+
of PHP attributes in your projects - however they will likely be deprecated and removed in the future.
7+
8+
Step Annotations
9+
----------------
10+
11+
Here is an example of how you can define your steps using annotations:
12+
13+
.. code-block:: php
14+
15+
// features/bootstrap/FeatureContext.php
16+
17+
use Behat\Behat\Context\Context;
18+
19+
class FeatureContext implements Context
20+
{
21+
/*
22+
* @Given we have some context
23+
*/
24+
public function prepareContext()
25+
{
26+
// do something
27+
}
28+
29+
/*
30+
* @When an :event occurs
31+
*/
32+
public function onEvent(string $event)
33+
{
34+
// do something
35+
}
36+
37+
/*
38+
* @Then something should be done
39+
*/
40+
public function checkOutcomes()
41+
{
42+
// do something
43+
}
44+
}
45+
46+
The pattern that you would include as an argument to the step attribute should be listed here
47+
after the annotation, separated by a space
48+
49+
Hook Annotations
50+
----------------
51+
52+
Here is an example of how you can define your hooks using annotations:
53+
54+
.. code-block:: php
55+
56+
// features/bootstrap/FeatureContext.php
57+
58+
use Behat\Behat\Context\Context;
59+
use Behat\Testwork\Hook\Scope\BeforeSuiteScope;
60+
use Behat\Behat\Hook\Scope\AfterScenarioScope;
61+
62+
class FeatureContext implements Context
63+
{
64+
/*
65+
* @BeforeSuite
66+
*/
67+
public static function prepare(BeforeSuiteScope $scope)
68+
{
69+
}
70+
71+
/*
72+
* @AfterScenario @database
73+
*/
74+
public function cleanDB(AfterScenarioScope $scope)
75+
{
76+
}
77+
}
78+
79+
Transformation Annotations
80+
--------------------------
81+
82+
Here is an example of how you can define your transformations using annotations:
83+
84+
.. code-block:: php
85+
86+
// features/bootstrap/FeatureContext.php
87+
88+
use Behat\Behat\Context\Context;
89+
90+
class FeatureContext implements Context
91+
{
92+
/*
93+
* @Transform /^(\d+)$/
94+
*/
95+
public function castStringToNumber($string)
96+
{
97+
return intval($string);
98+
}
99+
}
100+
101+
Existing code
102+
-------------
103+
104+
Even though annotations are still available, they will probably be deprecated and eventually removed in the future.
105+
Therefore, we do not recommend using annotations for new projects. If your current project uses annotations, we
106+
recommend that you refactor your code to use PHP attributes instead. This can be done manually but you should be able
107+
to use the search and replace capabilities of your IDE to do this in a more automated way.
108+
109+
Alternatively you may want to use a tool like `Rector`_ which can do automated refactoring of your code. Rector 2.0
110+
includes a rule that allows you to automatically convert any doc-block annotations to the corresponding attributes.
111+
To use it for your Behat contexts, add the following option to your Rector configuration:
112+
113+
.. code-block:: php
114+
115+
->withAttributesSets(behat: true)
116+
117+
.. _`Rector`: https://github.com/rectorphp/rector
118+

0 commit comments

Comments
 (0)