Open Vic152 opened 5 years ago
Hi,
You should define scenario outlines in following way:
Scenario Outline: Some outline
Given something
When <thing> is moved to <position>
Then something else
Examples:
| thing | position |
| 1 | 1 |
As you can see thing
and position
are in between <>
I don't know if you can use scenario outlines together with parsers. Example in documentation shows another way of specifying converters. In your case:
@scenario(
'yours.feature',
'Some outline',
example_converters=dict(thing=int, position=int)
)
def test_outlined():
pass
...
@when('<thing> is moved to <position>')
def thing_is_moved_to_position(thing, position):
assert isinstance(thing, int)
assert isinstance(position, int)
Good to know there is a way to do this like below:
@scenario( 'yours.feature', 'Some outline', example_converters=dict(thing=int, position=int) )
I may cast them too... I just thought you could use parser to post-process them. I think there is a value to what you proposed if u use <thing>
in several steps.
Thanks a lot!
As you can see
thing
andposition
are in between<>
Sorry formatting in GitHub
3rd edit: Also, it would be nice if example_converters
were mentioned in parser section of the documentation :)
Hi, I did get the same error when I was trying to combine "step arguments" with "scenario outlines".
I'm not 100% sure if this is a correct way of doing it, but with pytest-bdd==3.1.0
I manage to make this working like so:
Scenario Outline: Basic DuckDuckGo API Query
Given the DuckDuckGo API is queried with "<phrase>" using "json" format
Then the response status code is "200"
And the response contains results for "<phrase>"
Examples: Animals
| phrase |
| panda |
| python |
| platypus |
Examples: Fruits
| phrase |
| peach |
| pineapple |
| papaya |
scenarios('../features/service.feature', example_converters=dict(phrase=str))
@given(parsers.parse('the DuckDuckGo API is queried with "<phrase>" using "{fmt}" format'))
def ddg_response(phrase, fmt):
params = {'q': phrase, 'format': fmt}
response = requests.get(DUCKDUCKGO_API, params=params)
return response
@then('the response contains results for "<phrase>"')
def ddg_response_contents(ddg_response, phrase):
assert phrase.lower() == ddg_response.json()['Heading'].lower()
@then(parsers.parse('the response status code is "{code:d}"'))
def ddg_response_code(ddg_response, code):
assert ddg_response.status_code == code
Can anyone confirm if this is the way to do this or am I just lucky that it works as I want it? :)
@Evolter I can confirm that parse
is compatible with scenario outlines. For me, the problem arose trying to migrate steps from behave
to pytest-bdd
.
Take this example:
Scenario Outline: Animals are cute
Given a <animal>
When the <animal> is a baby
Then the <animal> is cute
Examples:
| animal |
| dog |
| cat |
Scenario: Some animals are cuter
Given a puppy
When the puppy is a baby
Then the puppy is cuter than cats
In behave
, you could represent both of these features with just a few steps:
from behave import given, when, then
@given('a {}')
def step_impl(context, animal):
pass
@when('the {} is a baby')
def step_impl(context, animal):
pass
@then('the {} is cute')
def step_impl(context, animal):
pass
@then('the {} is cuter than {}')
def step_impl(context, animal, other_animal):
pass
However, in pytest-bdd
, the order of operations for translating .feature
s to steps is different. You need to use step aliases to achieve the same result:
from pytest_bdd import given, when, scenarios, then
from pytest_bdd.parsers import parse
scenarios('animals.feature')
@given(parse('a {animal}'))
@given('a <animal>')
def animal(animal):
pass
@when(parse('the {animal} is a baby'))
@when('the <animal> is a baby')
def animal_is_baby(animal):
pass
@then(parse('the {animal} is cute'))
@then('the <animal> is cute')
def animal_is_cute(animal):
pass
@then(parse('the {animal} is cuter than {other_animal}'))
def animal_is_cuter(animal, other_animal):
pass
Personally I prefer the capabilities behave
in this case, but it's not a big deal to have to add a bit of aliasing. It just makes the step code a little less DRY.
I have also found the above, which feels really silly to me.
In addition to the syntactic ugliness of having to decorate evey step function twice, if you want it to work with a tabulated 'scenario outline' syntax in addition to the standlone 'scenario' syntax, it also doesn't properly respect parameter names. By this I mean you are forced to use the identical parameter names in your python step functions as is used in the outline. In my mind, this is unnecessary friction.
For example, if a tester writes
Scenario: SingleInput Validation
Given I am on the patient registration page
When I enter 12345 into mobilePhone
And I lose focus
Then I should see an error state on the mobilePhone field
and
Scenario Outline: Input Validation
Given I am on the patient registration page
When I enter <foo> into <field_name>
And I lose focus
Then I should see an error state on the <field_name> field
Examples:
| field_name | foo |
| mobilePhone | 01234 |
| homePhone | 12391 |
| email | rjiojr |
and the step is defined as
@when(parse("I enter {value} into {field_name}"))
@when("I enter <value> into <field_name>")
def enter_form_value(page_model: BasePage, value: str, field_name: str) -> None:
page_model.modify_form_field(field_name=field_name, value=value)
.. then the second situation fails, as 'foo' is required in the signature of enter_form_value
.
In my mind, the solution is for the scenario outline to "preprocess" the feature file before the individual lines are parsed. This would solve both problems: the proper parser decorator would work for everything, and the gherkin administrators can use any placeholder text for the dynamic aspects of the BDD. Even better, the aspects of the BDD step which are parameterised would not even need to correspond to single step functions! It may be that there are a collection of different step functions with similar but not identical BDD step text; this would allow the testers to refer to different step functions from the same outline!
I will attempt to resolve this and make a PR.
https://github.com/pytest-dev/pytest-bdd/pull/302
The tests demonstrate what I've done. Here you can see the given/when/then steps defined using the normal parse decorator. Note the parameter names: https://github.com/nicois/pytest-bdd/blob/master/tests/feature/test_outline.py#L164
Now compare that to the syntax in the scenario outline. There is no longer a need for the names used in the outlines to be the same as the step functions' argument names. You are even free to define your steps completely separately, as long as they have a common syntax which matches! https://github.com/nicois/pytest-bdd/blob/master/tests/feature/outline_modern.feature
Spending some more time with this, I'm having second thoughts about whether the above PR is an overall step forward (pun intended).
The issue is that if we implement the above, you then need to take a lot more care with the syntax of scenario outlines. For example. the following syntax will not work as cleanly, particularly if some of the parameters can be empty strings:
given a <foo> <bar> <baz>
you would need to add delimiters/quotes or similar to the BDD syntax to support this:
given a '<foo>' '<bar>' '<baz>'
... which won't play nicely with quotes in the actual argument values.
In short, the way I'm seeing it now, the price we would pay with my PR is probably too high to warrant it. I really don't like the dual wrapping of step functions referred to both from scenarios and scenario outlines, but I think we're stuck with it.
Partly in response to this problem, I have started to make an alternative pytest plugin to support gherkin files. It attempts to address a few shortcomings of pytest-bdd, such as the problem of mixing steps in outlines and non-outlines. If you're interested in seeing the direction I am headed, take a look here: https://github.com/nicois/pytest-gherkin/tree/develop/tests
Dear pytest-bdd team!
I'm about to start a larger test project with bdd style testing. In pytest-bdd, the scenario outline handling was a show stopper for me.
I was thinking to re-write it, but then I found the solution of @nicois . I tested it with my test code base and it worked perfectly for me.
Regarding the last doubt of @nicois (given a
This problem also affects our team. Migrating from squish (which is behave-like considering the BDD logic) We'd very much like to use the same step definition for both following cases.
Then picture on scene is '<file_name>'
Then picture on scene is 'test20878/result1.png'
Hi. New user here. I think I may be having a related issue.
I have the following feature definition:
Feature: Data
A program that handles various data.
Background:
Given a Data subclass
Scenario Outline: Instantiating a data subclass
When a <method> method is undefined
Then raise a TypeError on instantiation
Examples:
| method |
| download |
When I run:
@when('a <method> method is undefined')
def method_is_undefined(data_subclass, method):
assert method in data_subclass.__dict__['__abstractmethods__']
I get the aforementioned pytest_bdd.exceptions.StepDefinitionNotFoundError
error.
How can I fix this?
Thank you.
I think you are missing Given
in your Scenario Outline
. Try to put Given
in the scenario outline and let us know if it worked. I would probably not use Background
for scenario that you showed us.
Hey Guys,
I have a step in scenario outline:
In my glue code I write @when(parsers.parse('thing {thing:d} is moved to position {position:d}'))
I when an error: pytest_bdd.exceptions.StepDefinitionNotFoundError: Step definition is not found: When
I tried When thing {thing} is moved to position {position}
When I use is passes it as a string. I though if I use parse I could make it an int. Is there some special way feature file has to be written?