Skip to content

Commit 5f74f1f

Browse files
committed
Document Python usage
1 parent fc9456e commit 5f74f1f

File tree

2 files changed

+125
-38
lines changed

2 files changed

+125
-38
lines changed

README.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,7 @@ Then this expression would match the following example:
224224
I have 1 \{what} cucumber in my belly \(amazing!)
225225
I have 42 \{what} cucumbers in my belly \(amazing!)
226226

227-
There is currently no way to escape a `/` character - it will always be interpreted
228-
as alternative text.
227+
The `/` character will always be interpreted as an alternative, unless escaped, such as with `\/`.
229228

230229
## Architecture
231230

python/README.md

+124-36
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,96 @@
55
![LICENSE](https://img.shields.io/badge/license-MIT-blue)
66
[![test-python](https://github.com/cucumber/cucumber-expressions/actions/workflows/test-python.yml/badge.svg)](https://github.com/cucumber/cucumber-expressions/actions/workflows/test-python.yml)
77

8-
For detailed documentation on Cucumber Expressions, visit the [main docs](https://github.com/cucumber/cucumber-expressions#readme).
8+
For detailed documentation on __Cucumber Expressions__, visit the [main docs](https://github.com/cucumber/cucumber-expressions#readme).
99

10-
Cucumber Expressions are presently unsupported natively by common Python frameworks such as [behave](https://behave.readthedocs.io) and [pytest-bdd](https://pytest-bdd.readthedocs.io). However support can be integrated.
10+
## Installation
1111

12-
> For pytest-bdd, see the [pytest-bdd-ng](https://pytest-bdd-ng.readthedocs.io) fork and its documentation on [how to use Cucumber Expressions](https://pytest-bdd-ng.readthedocs.io/en/default/#step-parameters). It should be noted that this fork is out of date with the latest releases.
12+
__Cucumber Expressions__ is available via [PyPI](https://pypi.org/project/cucumber-expressions/):
1313

14-
## Using Cucumber Expressions with Behave
14+
```console
15+
pip install cucumber-expressions
16+
```
1517

16-
Native support for Cucumber Expressions in _behave_ is presently unavailable. To enable support, a compatible step matcher must be defined and patched into its matchers. The following steps walk through this process.
18+
## Usage
1719

18-
```console
19-
pip install cuke4behave
20-
pip install "cucumber-expressions>17.0.1"
20+
Cucumber Expressions can be used in numerous contexts as a human-friendly substitution for Regular Expressions.
21+
22+
```python
23+
>>> from cucumber_expressions.expression import CucumberExpression
24+
>>> from cucumber_expressions.parameter_type_registry import ParameterTypeRegistry
25+
>>> registry = ParameterTypeRegistry()
26+
>>> expression = CucumberExpression("I have {int} cucumbers in my belly", registry)
27+
>>> expression.regexp
28+
'^I have ((?:-?\\d+)|(?:\\d+)) cucumbers in my belly$'
29+
>>> match = expression.match("I have 42 cucumbers in my belly")
30+
>>> match
31+
[<cucumber_expressions.argument.Argument object at 0x10dcf7650>]
32+
>>> argument = match[0]
33+
>>> argument.value
34+
42
2135
```
2236

23-
Define a Cucumber Expressions step matcher inside `environment.py` in your `features` directory.
37+
### Using Cucumber Expressions with Behave
38+
39+
Native support for Cucumber Expressions in [behave](https://behave.readthedocs.io) is presently unavailable. To enable support, a compatible step matcher must be defined and patched into Behave's matchers. The following steps walk through this process.
40+
41+
Define a Cucumber Expressions step matcher for Behave inside `cucumber_matcher.py` in your `features` directory.
2442

2543
```python
26-
from behave.matchers import matcher_mapping, use_step_matcher
44+
from behave.matchers import Matcher
45+
from behave.model_core import Argument
46+
from cucumber_expressions.expression import CucumberExpression
47+
48+
49+
class CucumberExpressionMatcher(Matcher):
50+
51+
def __init__(
52+
self,
53+
func,
54+
pattern,
55+
step_type= None,
56+
parameter_type_registry = None,
57+
):
58+
super().__init__(func, pattern, step_type)
59+
self.func = func
60+
self.pattern = pattern
61+
self.parameter_type_registry = parameter_type_registry
62+
self.__cucumber_expression_ = CucumberExpression(
63+
pattern, self.parameter_type_registry
64+
)
65+
66+
def check_match(self, step):
67+
result = self.__cucumber_expression_.match(step)
68+
if result is None:
69+
return None
70+
return [
71+
Argument(x.group.start, x.group.end, str(x.value), x.value)
72+
for x in result
73+
]
74+
75+
@property
76+
def regex_pattern(self):
77+
self.__cucumber_expression_.regexp
78+
79+
80+
def build_step_matcher(parameter_type_registry):
81+
def step_matcher(func, pattern):
82+
return CucumberExpressionMatcher(
83+
func,
84+
pattern,
85+
parameter_type_registry=parameter_type_registry
86+
)
87+
return step_matcher
88+
89+
```
90+
91+
Import and patch the matcher into Behave inside `environment.py` in your `features` directory.
92+
93+
```python
94+
from behave.matchers import use_step_matcher, matcher_mapping
2795
from cucumber_expressions.parameter_type_registry import ParameterTypeRegistry
28-
from cuke4behave.step_matcher import build_step_matcher
96+
97+
from cucumber_matcher import build_step_matcher
2998

3099
# Initialise a Cucumber Expressions parameter registry
31100
parameter_registry = ParameterTypeRegistry()
@@ -48,60 +117,71 @@ Feature: Color selection
48117
Rule: User can select a profile color
49118
50119
Scenario: User selects a valid color
51-
Given I am on the profile customization page
52-
When I select the color "red"
53-
Then the profile color should change to "red"
120+
Given I am on the profile settings page
121+
When I select the theme colour "red"
122+
Then the profile colour should be "red"
54123
```
55124

56125
Create step definitions inside `color.py` in your `features/steps` directory:
57126

58127
```python
59-
from behave import given, when, then
60-
from behave.matchers import matcher_mapping
128+
from behave import given, then, when
61129
from cucumber_expressions.parameter_type import ParameterType
62130

131+
from environment import parameter_registry
132+
63133
# Define the parameter type
64134
color = ParameterType(
65135
name="color",
66136
regexp="red|blue|yellow",
67137
type=str,
68138
transformer=lambda s: s,
139+
use_for_snippets=True,
140+
prefer_for_regexp_match=False,
69141
)
70142

71143
# Pass the parameter type to the registry instance
72-
matcher = matcher_mapping["cucumber_expressions"](None, "")
73-
parameter_registry = matcher.parameter_type_registry
74144
parameter_registry.define_parameter_type(color)
75145

76-
77-
@given("I am on the profile customization page")
78-
def step_given(context, page):
146+
@given("I am on the profile customisation/settings page")
147+
def step_given(context):
79148
assert True
80149

81-
82150
# Reference the parameter type in the step definition pattern
83-
@when('I select the color "{color}"')
84-
def step_when(context, color):
151+
@when('I select the theme colo(u)r "{color}"')
152+
def step_when(context, selected_color):
85153
assert color
86-
context.selected_color = color
87-
154+
context.selected_color = selected_color
88155

89-
@then('the profile color should change to "{color}"')
90-
def step_then(context, color):
156+
@then('the profile colo(u)r should be "{color}"')
157+
def step_then(context, displayed_color):
91158
assert color
92-
assert context.selected_color == color
159+
assert context.selected_color == displayed_color
160+
161+
```
162+
163+
The necessary files are now in place to execute our gherkin scenario.
164+
165+
```console
166+
repository/
167+
└── features/
168+
├── steps/
169+
│ └── color.py
170+
├── cucumber_matcher.py
171+
├── environment.py
172+
└── color.feature
93173
```
94174

95175
Finally, execute Behave. The scenario will run with the step definitions using the Cucumber Expressions parameter type.
96176

97177
```console
98-
behave
99-
Feature: Color selection # features/color.feature:1
178+
$ behave features
179+
Feature: Color selection # features/Gherkin.feature:1
100180
Rule: User can select a profile color
101-
Scenario: User selects a valid color # features/color.feature:5
102-
Given I am on the profile customization page # features/steps/color.py:21 0.000s
103-
When I select the color "red" # features/steps/color.py:27 0.000s
104-
Then the profile color should change to "red" # features/steps/color.py:33 0.000s
181+
Scenario: User selects a valid color # features/Gherkin.feature:5
182+
Given I am on the profile settings page # features/steps/color.py:20 0.000s
183+
When I select the theme colour "red" # features/steps/color.py:26 0.000s
184+
Then the profile colour should be "red" # features/steps/color.py:32 0.000s
105185

106186
1 feature passed, 0 failed, 0 skipped
107187
1 scenario passed, 0 failed, 0 skipped
@@ -111,4 +191,12 @@ Took 0m0.001s
111191

112192
For detailed usage of _behave_, see the [official documentation](https://behave.readthedocs.io).
113193

114-
> Note: Alternatives and optionals are currently unsupported with the above example.
194+
### Using Cucumber Expressions with pytest-bdd
195+
196+
Native support for Cucumber Expressions in [pytest-bdd](https://pytest-bdd.readthedocs.io) is presently unavailable. However the [pytest-bdd-ng](https://pytest-bdd-ng.readthedocs.io) fork provides integrated support. Follow its documentation on [how to use Cucumber Expressions](https://pytest-bdd-ng.readthedocs.io/en/default/#step-parameters).
197+
198+
> It should be noted that _pytest-bdd-ng_ is out of date with the latest releases of pytest-bdd.
199+
200+
## License
201+
202+
__Cucumber Expressions__ is licensed under the MIT License.

0 commit comments

Comments
 (0)