Skip to content

Commit

Permalink
Bare async step implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
youtux committed Jul 23, 2023
1 parent 0fab820 commit d12403e
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 2 deletions.
20 changes: 19 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ types-setuptools = "^65.5.0.2"
pytest-xdist = "^3.0.2"
coverage = {extras = ["toml"], version = "^6.5.0"}
Pygments = "^2.13.0" # for code-block highlighting
pytest-asyncio = "^0.21.1"

[build-system]
requires = ["poetry-core>=1.0.0"]
Expand Down
18 changes: 17 additions & 1 deletion src/pytest_bdd/scenario.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
"""
from __future__ import annotations

import asyncio
import contextlib
import functools
import logging
import os
import re
Expand Down Expand Up @@ -120,6 +122,19 @@ def get_step_function(request, step: Step) -> StepFunctionContext | None:
return None


def ensure_sync(fn):
"""Convert async function to sync function."""
__tracebackhide__ = True
if not asyncio.iscoroutinefunction(fn):
return fn

@functools.wraps(fn)
def wrapper(*args, **kwargs):
return asyncio.run(fn(*args, **kwargs))

return wrapper


def _execute_step_function(
request: FixtureRequest, scenario: Scenario, step: Step, context: StepFunctionContext
) -> None:
Expand Down Expand Up @@ -156,7 +171,8 @@ def _execute_step_function(

request.config.hook.pytest_bdd_before_step_call(**kw)
# Execute the step as if it was a pytest fixture, so that we can allow "yield" statements in it
return_value = call_fixture_func(fixturefunc=context.step_func, request=request, kwargs=kwargs)
step_func = ensure_sync(context.step_func)
return_value = call_fixture_func(fixturefunc=step_func, request=request, kwargs=kwargs)
except Exception as exception:
request.config.hook.pytest_bdd_step_error(exception=exception, **kw)
raise
Expand Down
69 changes: 69 additions & 0 deletions tests/library/test_async.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import textwrap


# TODO: Split this test in one that checks that we work correctly
# with the pytest-asyncio plugin, and another that checks that we correctly
# run async steps.
def test_async_steps(pytester):
"""Test parent given is collected.
Both fixtures come from the parent conftest.
"""
pytester.makefile(
".feature",
async_feature=textwrap.dedent(
"""\
Feature: A feature
Scenario: A scenario
Given There is an async object
When I do an async action
Then the async object value should be "async_object"
And [async] the async object value should be "async_object"
And the another async object value should be "another_async_object"
"""
),
)

pytester.makepyfile(
textwrap.dedent(
"""\
from pytest_bdd import given, parsers, scenarios, then, when
import asyncio
import pytest
scenarios("async_feature.feature")
@pytest.fixture
async def another_async_object():
await asyncio.sleep(0.01)
return "another_async_object"
@given("There is an async object", target_fixture="async_object")
async def given_async_obj():
await asyncio.sleep(0.01)
return "async_object"
@when("I do an async action")
async def when_i_do_async_action():
await asyncio.sleep(0.01)
@then(parsers.parse('the async object value should be "{value}"'))
async def the_sync_object_value_should_be(async_object, value):
assert async_object == value
@then(parsers.parse('[async] the async object value should be "{value}"'))
async def async_the_async_object_value_should_be(async_object, value):
await asyncio.sleep(0.01)
assert async_object == value
@then(parsers.parse('the another async object value should be "{value}"'))
def the_another_async_object_value_should_be(another_async_object, value):
assert another_async_object == value
"""
)
)
result = pytester.runpytest("--asyncio-mode=auto")
result.assert_outcomes(passed=1)

0 comments on commit d12403e

Please sign in to comment.