pytest-bdd/tests/library/test_parent.py

407 lines
11 KiB
Python

"""Test givens declared in the parent conftest and plugin files.
Check the parent givens are collected and overridden in the local conftest.
"""
import textwrap
from pytest_bdd.utils import collect_dumped_objects
def test_parent(pytester):
"""Test parent given is collected.
Both fixtures come from the parent conftest.
"""
pytester.makefile(
".feature",
parent=textwrap.dedent(
"""\
Feature: Parent
Scenario: Parenting is easy
Given I have a parent fixture
And I have an overridable fixture
"""
),
)
pytester.makeconftest(
textwrap.dedent(
"""\
from pytest_bdd import given
@given("I have a parent fixture", target_fixture="parent")
def _():
return "parent"
@given("I have an overridable fixture", target_fixture="overridable")
def _():
return "parent"
"""
)
)
pytester.makepyfile(
textwrap.dedent(
"""\
from pytest_bdd import scenario
@scenario("parent.feature", "Parenting is easy")
def test_parent(request):
assert request.getfixturevalue("parent") == "parent"
assert request.getfixturevalue("overridable") == "parent"
"""
)
)
result = pytester.runpytest()
result.assert_outcomes(passed=1)
def test_global_when_step(pytester):
"""Test when step defined in the parent conftest."""
pytester.makefile(
".feature",
global_when=textwrap.dedent(
"""\
Feature: Global when
Scenario: Global when step defined in parent conftest
When I use a when step from the parent conftest
"""
),
)
pytester.makeconftest(
textwrap.dedent(
"""\
from pytest_bdd import when
from pytest_bdd.utils import dump_obj
@when("I use a when step from the parent conftest")
def _():
dump_obj("global when step")
"""
)
)
pytester.mkpydir("subdir").joinpath("test_global_when.py").write_text(
textwrap.dedent(
"""\
from pytest_bdd import scenarios
scenarios("../global_when.feature")
"""
)
)
result = pytester.runpytest("-s")
result.assert_outcomes(passed=1)
[collected_object] = collect_dumped_objects(result)
assert collected_object == "global when step"
def test_child(pytester):
"""Test the child conftest overriding the fixture."""
pytester.makeconftest(
textwrap.dedent(
"""\
from pytest_bdd import given
@given("I have a parent fixture", target_fixture="parent")
def _():
return "parent"
@given("I have an overridable fixture", target_fixture="overridable")
def main_conftest():
return "parent"
"""
)
)
subdir = pytester.mkpydir("subdir")
subdir.joinpath("conftest.py").write_text(
textwrap.dedent(
"""\
from pytest_bdd import given
@given("I have an overridable fixture", target_fixture="overridable")
def subdir_conftest():
return "child"
"""
)
)
subdir.joinpath("child.feature").write_text(
textwrap.dedent(
"""\
Feature: Child
Scenario: Happy childhood
Given I have a parent fixture
And I have an overridable fixture
"""
),
)
subdir.joinpath("test_library.py").write_text(
textwrap.dedent(
"""\
from pytest_bdd import scenario
@scenario("child.feature", "Happy childhood")
def test_override(request):
assert request.getfixturevalue("parent") == "parent"
assert request.getfixturevalue("overridable") == "child"
"""
)
)
result = pytester.runpytest()
result.assert_outcomes(passed=1)
def test_local(pytester):
"""Test locally overridden fixtures."""
pytester.makeconftest(
textwrap.dedent(
"""\
from pytest_bdd import given
@given("I have a parent fixture", target_fixture="parent")
def _():
return "parent"
@given("I have an overridable fixture", target_fixture="overridable")
def _():
return "parent"
"""
)
)
subdir = pytester.mkpydir("subdir")
subdir.joinpath("local.feature").write_text(
textwrap.dedent(
"""\
Feature: Local
Scenario: Local override
Given I have a parent fixture
And I have an overridable fixture
"""
),
)
subdir.joinpath("test_library.py").write_text(
textwrap.dedent(
"""\
from pytest_bdd import given, scenario
@given("I have an overridable fixture", target_fixture="overridable")
def _():
return "local"
@given("I have a parent fixture", target_fixture="parent")
def _():
return "local"
@scenario("local.feature", "Local override")
def test_local(request):
assert request.getfixturevalue("parent") == "local"
assert request.getfixturevalue("overridable") == "local"
"""
)
)
result = pytester.runpytest()
result.assert_outcomes(passed=1)
def test_uses_correct_step_in_the_hierarchy(pytester):
"""
Test regression found in issue #524, where we couldn't find the correct step implemntation in the
hierarchy of files/folder as expected.
This test uses many files and folders that act as decoy, while the real step implementation is defined
in the last file (test_b/test_b.py).
"""
pytester.makefile(
".feature",
specific=textwrap.dedent(
"""\
Feature: Specificity of steps
Scenario: Overlapping steps
Given I have a specific thing
Then pass
"""
),
)
pytester.makeconftest(
textwrap.dedent(
"""\
from pytest_bdd import parsers, given, then
from pytest_bdd.utils import dump_obj
import pytest
@given(parsers.re("(?P<thing>.*)"))
def root_conftest_catchall(thing):
dump_obj(thing + " (catchall) root_conftest")
@given(parsers.parse("I have a {thing} thing"))
def root_conftest(thing):
dump_obj(thing + " root_conftest")
@given("I have a specific thing")
def root_conftest_specific():
dump_obj("specific" + "(specific) root_conftest")
@then("pass")
def _():
pass
"""
)
)
# Adding deceiving @when steps around the real test, so that we can check if the right one is used
# the right one is the one in test_b/test_b.py
# We purposefully use test_a and test_c as decoys (while test_b/test_b is "good one"), so that we can test that
# we pick the right one.
pytester.makepyfile(
test_a="""\
from pytest_bdd import given, parsers
from pytest_bdd.utils import dump_obj
@given(parsers.re("(?P<thing>.*)"))
def in_root_test_a_catch_all(thing):
dump_obj(thing + " (catchall) test_a")
@given(parsers.parse("I have a specific thing"))
def in_root_test_a_specific():
dump_obj("specific" + " (specific) test_a")
@given(parsers.parse("I have a {thing} thing"))
def in_root_test_a(thing):
dump_obj(thing + " root_test_a")
"""
)
pytester.makepyfile(
test_c="""\
from pytest_bdd import given, parsers
from pytest_bdd.utils import dump_obj
@given(parsers.re("(?P<thing>.*)"))
def in_root_test_c_catch_all(thing):
dump_obj(thing + " (catchall) test_c")
@given(parsers.parse("I have a specific thing"))
def in_root_test_c_specific():
dump_obj("specific" + " (specific) test_c")
@given(parsers.parse("I have a {thing} thing"))
def in_root_test_c(thing):
dump_obj(thing + " root_test_b")
"""
)
test_b_folder = pytester.mkpydir("test_b")
# More decoys: test_b/test_a.py and test_b/test_c.py
test_b_folder.joinpath("test_a.py").write_text(
textwrap.dedent(
"""\
from pytest_bdd import given, parsers
from pytest_bdd.utils import dump_obj
@given(parsers.re("(?P<thing>.*)"))
def in_root_test_b_test_a_catch_all(thing):
dump_obj(thing + " (catchall) test_b_test_a")
@given(parsers.parse("I have a specific thing"))
def in_test_b_test_a_specific():
dump_obj("specific" + " (specific) test_b_test_a")
@given(parsers.parse("I have a {thing} thing"))
def in_test_b_test_a(thing):
dump_obj(thing + " test_b_test_a")
"""
)
)
test_b_folder.joinpath("test_c.py").write_text(
textwrap.dedent(
"""\
from pytest_bdd import given, parsers
from pytest_bdd.utils import dump_obj
@given(parsers.re("(?P<thing>.*)"))
def in_root_test_b_test_c_catch_all(thing):
dump_obj(thing + " (catchall) test_b_test_c")
@given(parsers.parse("I have a specific thing"))
def in_test_b_test_c_specific():
dump_obj("specific" + " (specific) test_a_test_c")
@given(parsers.parse("I have a {thing} thing"))
def in_test_b_test_c(thing):
dump_obj(thing + " test_c_test_a")
"""
)
)
# Finally, the file with the actual step definition that should be used
test_b_folder.joinpath("test_b.py").write_text(
textwrap.dedent(
"""\
from pytest_bdd import scenarios, given, parsers
from pytest_bdd.utils import dump_obj
scenarios("../specific.feature")
@given(parsers.parse("I have a {thing} thing"))
def in_test_b_test_b(thing):
dump_obj(f"{thing} test_b_test_b")
"""
)
)
test_b_folder.joinpath("test_b_alternative.py").write_text(
textwrap.dedent(
"""\
from pytest_bdd import scenarios, given, parsers
from pytest_bdd.utils import dump_obj
scenarios("../specific.feature")
# Here we try to use an argument different from the others,
# to make sure it doesn't matter if a new step parser string is encountered.
@given(parsers.parse("I have a {t} thing"))
def in_test_b_test_b(t):
dump_obj(f"{t} test_b_test_b")
"""
)
)
result = pytester.runpytest("-s")
result.assert_outcomes(passed=2)
[thing1, thing2] = collect_dumped_objects(result)
assert thing1 == thing2 == "specific test_b_test_b"