All,
TL;DR - How do I programmatically (e.g. inside pytest_generate_tests) wrap
a test function with a wrapper function that accept an additional fixture?
Very long version
I was on the verge to release version 1.0 of the pytest-nodev plugin [1]
that enable test-driven search via a fixture or via a decorator, when I
noticed that I could use the marker interface as a much nicer API, but I
couldn't figure out the last bit I need.
The context
Following the example in the README assume I need to write a `parse_bool`
function that robustly parses a boolean value from a string. Here is the
test I intend to use to validate my own implementation once I write it:
def test_parse_bool():
assert not parse_bool('false')
assert not parse_bool('FALSE')
assert not parse_bool('0')
assert parse_bool('true')
assert parse_bool('TRUE')
assert parse_bool('1')
I can "instrument" the test to use with pytest-nodev in two ways, first
using the"wish" fixture explicitly:
def test_parse_bool(wish): # <- here...
parse_bool = wish # <- ... and here
assert not parse_bool('false')
assert not parse_bool('FALSE')
assert not parse_bool('0')
assert parse_bool('true')
assert parse_bool('TRUE')
assert parse_bool('1')
the search is the executed with:
py.test test_parse_bool.py --wish-from-stdlib
The test is run once for every function found in the standard library and
the functions that make the test pass (the little gem that
is distutils.util:strtobool) is presented in the result summary.
This is working right now, but the user needs to modify its test in a non
trivial way. For the curious I parametrize the metafunc inside
pytest_generate_tests [2].
So I'm experimenting with a following decorator interface:
import pytest_nodev
@pytest_nodev.search('parse_bool')
def test_parse_bool():
assert not parse_bool('false')
assert not parse_bool('FALSE')
assert not parse_bool('0')
assert parse_bool('true')
assert parse_bool('TRUE')
assert parse_bool('1')
The "search" decorator takes the name of the tested object and uses the
"monkeypatch" fixture together with the "wish" fixture to have the same
result as above. This works right now and it is much easier for the users.
The implementation is more complex though [3].
This is still not perfect because you still need to prepare the test
specifically to be used for a search with pytest_nodev.
The best approach would be to use a marker:
import pytest
@pytest.mark.target('parse_bool')
def test_parse_bool():
assert not parse_bool('false')
assert not parse_bool('FALSE')
assert not parse_bool('0')
assert parse_bool('true')
assert parse_bool('TRUE')
assert parse_bool('1')
The marker is a nice documentation and can be left even when pytest-nodev
is not installed.
The problem is I didn't find a way to use the marker to monkeypatch and
parametrize the test. This is the closest I could get:
def pytest_generate_tests(metafunc):
search_marker = getattr(metafunc.function, 'search', None)
if not search_marker:
return
# setup the free variables for the wrapper closure
target_name = search_marker.args[0]
test_func = metafunc.function
def wrapper(wish, monkeypatch):
monkeypatch.setattr(target_name, wish)
return test_func()
# trying to make a hand-made test decorator
metafunc.function = wrapper
ids, params = make_wish_index(metafunc.config)
metafunc.parametrize('wish', params, ids=ids, scope='module')
But this dies with:
pytest_nodev/plugin.py:134: in pytest_generate_tests
metafunc.parametrize('wish', params, ids=ids, scope='module')
../mac-cpython3/lib/python3.5/site-packages/_pytest/python.py:1000: in
parametrize
raise ValueError("%r uses no fixture %r" %(self.function, arg))
E ValueError: <function pytest_generate_tests.<locals>.wrapper at
0x1064b7400> uses no fixture 'wish'
Obviously I didn't manage to simulate [2] inside the pytest_generate_tests.
I guess that I need to tell the metafunc that the function now use the wish
and the monkeypatch fixtures.
So my question is: can I programmatically simulate adding a decorator with
arbitrary fixtures to a test? Is there a better way to do it?
I understand the use case is complex and in case anybody is willing to help
I'll do my best to ease their work, like creating a dedicated branch in the
github repo.
Thanks,
Alessandro
[1]. https://github.com/nodev-io/pytest-nodev
[2].
https://github.com/nodev-io/pytest-nodev/blob/master/pytest_nodev/plugin.py#L121
[3].
https://github.com/nodev-io/pytest-nodev/blob/master/pytest_nodev/__init__.py#L12
_______________________________________________
pytest-dev mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pytest-dev