Question
How can I import helper functions in test files without creating packages in the test
directory?
Context
I'd like to create a test helper function that I can import in several tests. Say, something like this:
# In common_file.py
def assert_a_general_property_between(x, y):
# test a specific relationship between x and y
assert ...
# In test/my_test.py
def test_something_with(x):
some_value = some_function_of_(x)
assert_a_general_property_between(x, some_value)
Using Python 3.5, with py.test 2.8.2
Current "solution"
I'm currently doing this via importing a module inside my project's test
directory (which is now a package), but I'd like to do it with some other mechanism if possible (so that my test
directory doesn't have packages but just tests, and the tests can be run on an installed version of the package, as is recommended here in the py.test documentation on good practices).
__init__.py
-files but at the same time provide no alternative to sharing helper functions between tests. My hair is turning gray over this.
You could define a helper class in conftest.py, then create a fixture that returns that class (or an instance of it, depending on what you need).
import pytest
class Helpers:
@staticmethod
def help_me():
return "no"
@pytest.fixture
def helpers():
return Helpers
Then in your tests, you can use the fixture:
def test_with_help(helpers):
helpers.help_me()
my option is to create an extra dir in tests
dir and add it to pythonpath in the conftest so.
tests/
helpers/
utils.py
...
conftest.py
setup.cfg
in the conftest.py
import sys
import os
sys.path.append(os.path.join(os.path.dirname(__file__), 'helpers'))
in setup.cfg
[pytest]
norecursedirs=tests/helpers
this module will be available with import utils
, only be careful to name clashing.
test_module.py
using def helper_fun():
. As long as the helper function doesn't start with test_helper_fun
, it wont be collected by pytest. But it if you want to run it in a particular test function you can still call helper_func
. Is there a reason to complicate it as such?
test_module.py
, cant you just use import from tests.helper.util import helper_fun
to be able to access the helper function in the test_function?
tests
directory importable, so you cannot use your function from other modules
While searching for a solution for this problem I came across this SO question and ended up adopting the same approach. Creating a helpers package, munging sys.path
to make it importable and then just importing it...
This did not seem the best approach, so, I created pytest-helpers-namespace. This plugin allows you to register helper functions on your conftest.py
:
import pytest
pytest_plugins = ['helpers_namespace']
@pytest.helpers.register
def my_custom_assert_helper(blah):
assert blah
# One can even specify a custom name for the helper
@pytest.helpers.register(name='assertme')
def my_custom_assert_helper_2(blah):
assert blah
# And even namespace helpers
@pytest.helpers.asserts.register(name='me')
def my_custom_assert_helper_3(blah):
assert blah
And then, within a test case function body just use it like
def test_this():
assert pytest.helpers.my_custom_assert_helper(blah)
def test_this_2():
assert pytest.helpers.assertme(blah)
def test_this_3():
assert pytest.helpers.asserts.me(blah)
Its pretty simple and the documentation pretty small. Take a look and tell me if it addresses your problem too.
To access a method from different modules without creating packages, and have that function operate as a helper function I found the following helpful:
conftest.py:
@pytest.fixture
def compare_test_vs_actual():
def a_function(test, actual):
print(test, actual)
return a_function
test_file.py:
def test_service_command_add(compare_test_vs_actual):
compare_test_vs_actual("hello", "world")
def _compare_test_vs_actual(): pass
then @fixture;def compare_test_vs_actual():return _compare_test_vs_actual
. Clearer with less nesting, although if you want to receive a fixture in your fixture the above is probably cleaner.
Create a helpers package in tests folder:
tests/
helpers/
__init__.py
utils.py
...
# make sure no __init__.py in here!
setup.cfg
in setup.cfg:
[pytest]
norecursedirs=tests/helpers
the helpers will be available with import helpers
.
pytest.ini
as config file as recommended in official document.
It is possible that some of you will be completely uphold by my suggestion. However, very simple way of using common function or value from other modules is to inject it directly into a common workspace. Example: conftest.py:
import sys
def my_function():
return 'my_function() called'
sys.modules['pytest'].common_funct = my_function
test_me.py
import pytest
def test_A():
print(pytest.common_funct())
As another option, this directory structure worked for me:
mypkg/
...
test_helpers/
__init__.py
utils.py
...
tests/
my_test.py
...
And then in my_test.py
import the utilities using: from test_helpers import utils
test_helpers
as well, for instance.
Success story sharing
pytest_collection_modifyitems
?pytest.mark.usefixtures
to dynamically add the fixture by name to a test item during the collection phase.