Pytest-bdd: Importing common steps
Asked Answered
A

3

6

Edit: I am no longer working on this project, but I will leave this question open until answered in case it is of use to anyone.

I am working on implementing pytest-bdd and I am trying to import use steps from a different file named ui_shared.py.

Currently my directory is structured:

proj/lib/ui_file.py
proj/lib/ui_shared.py
proj/tests/test_file.py
proj/features/file.feature

Pytest-bdd is able to recognize the steps from ui_shared.py and execute the test as long as ui_file.py imports with:

from ui_shared import *

but I would like to avoid using import *.

I have tried import ui_shared.py and from ui_shared.py import common_step, where common_step is the step function I would like to import, and I get the error:

StepDefinitionNotFoundError: Step definition is not found: Given "common function".

There is a related question that I found: Behave: How to import steps from another file? as well as a few others, most of which says to import the steps into the common steps file, ui_shared.py in my case, which I have already done.

Here is the code for ui_shared.py:

#!/usr/bin/python

import pytest
from pytest_bdd import (
    scenario,
    given,
    when,
    then
)

@pytest.fixture(scope="function")
def context():
    return{}

@given('common step')
def common_step(input):
    #some method

Here are other relevant code:

In file.feature:

Scenario Outline: ui_file
    Given common step
    And another given step
    When some step
    Then last step

In test_file.py:

#!/usr/bin/python

@pytest.fixture
def pytestbdd_strict_gherkin():
    return False

@scenario('proj/features/file.feature', 'ui_file')
def test_ui_file():
    """ui_file"""

And in ui_file.py:

import pytest
from pytest_bdd import (
    scenario,
    given,
    when,
    then
)

from ui_shared import * #This is what I am trying to change

@given('another given step')
def another_given_step(input)
    #some method

@when('some step')
def some_step(input)
    #some method

@then('last step')
def last_step(input)
    #some method

The above should work as is, but if the import method is changed then the pytest fails with E StepDefinitionNotFoundError.

What I am looking for is a way to import all names defined in ui_shared.py except for the methods I am not using.

Basically, how do I import using from file import without the * and allow my ui_file.py to use the common steps from ui_shared.py?

Amick answered 10/9, 2018 at 21:18 Comment(5)
from ui_file import context, common_step?Bernita
@EPo I tried that as well, it returns the same error StepDefinitionNotFoundError: Step definition is not found: Given "common function".Amick
Perhaps you might need to carve out a minimal example (the relevant contents of other files) allowing to replicate the error and post it to question. Once you have it you can also raise an issue at project github repository, and get advice from maintainers and other users.Bernita
There are some StepDefinitionNotFoundError issues at github already, but they are somewhat different.Bernita
@EPo I edited my question with the contents of other relevant files, hopefully you can reproduce the error. I will take a look at their git repo as well, thanks for the suggestion!Amick
U
2

Add the following lines inside your conftest.py

pytest_plugins = [
   "lib.ui_shared"
]

This way all fixtures or pytest-bdd steps inside ui_shared.py will be available to every other file as if it was inside conftest.py

NOTE
lib must be a package, meaning there should be a __init__.py file inside the lib folder

Unchaste answered 2/7, 2021 at 12:46 Comment(0)
M
1

I just found a way to make this work, although it's not as elegant. If you change from ui_shared import * to from ui_shared import common_step and THEN you also add (given('common_step'))(common_step) in the same ui_file.py, then you will be able to run the test importing common_step and without using import *. So you would have this:

In ui_file.py:

import pytest
from pytest_bdd import (
    scenario,
    given,
    when,
    then
)

from ui_shared import common_step

(given('common_step'))(common_step)

@given('another given step')
def another_given_step(input)
    #some method

@when('some step')
def some_step(input)
    #some method

@then('last step')
def last_step(input)
    #some method

The rest of the files would remain unchanged.

Now, the reason why this works is unclear to me, but it looks like importing the decorated function doesn't work using the syntactic sugar for decorators and so you can use the imported steps only if you decorate the function explicitly (without the syntactic sugar with @).

EDIT: Updated paranthesis in added given lines.

Monocotyledon answered 11/1, 2023 at 20:25 Comment(1)
This is super cool. Not elegant but functional. Thank you! :)Campestral
R
0

You can try something like this:

from .ui_file import *

That is what I wrote for my project:

from .devices_steps import *
Rolon answered 4/3, 2020 at 10:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.