[pytest-dev] process for replacing yield tests with modern style
holger krekel
holger at merlinux.eu
Sat Oct 22 13:27:11 EDT 2016
Hi Chris, all,
maybe there is a solution ...
On Fri, Oct 21, 2016 at 13:03 +0100, Chris Dent wrote:
> Several years ago, when I started using pytest, the yield tests were
> my favorite thing about it. Super simple: push out functions that
> have assertions in them. That's what pytest was all about then, and
> it was _glorious_. In the intervening years things have become more
> complex. That's the nature of things.
I don't consider the discussion or development fully finished FWIW.
IIRC i came up with the idea of using yield for generating tests (also thus termed "generative tests"). I liked the simplicity of generating parametrized tests with it, each nicely represented by the typical ".", the primary conceptual unit of testing.
The main problem with yield-generated tests is that we call xUnit-setup/teardown methods which means we need to handle them during collection. Also it complicates the "Function" item, the builtin test item class which describes a test function, its parameters and handles it execution and failure representation. The complications could probably be reduced by separating some code out into a "YieldedFunction". But the former is a more fundamental problem and the reason why documentation for yield was mostly removed and there is a long standing recommendation to move towards the parametrize decorator or pytest_generate_tests if you need more dynamic or complex parametrization.
So today we have three mechanisms for generating tests which don't require a conftest.py:
- test functions using yield to generate func+param (deprecated)
- @pytest.mark.parametrize-decorated test functions which are executed
repeatedly with multiple argument sets.
- the pytest_generate_tests(metafunc) hook which allows to parametrize function
arguments or fixtures which will execute a test function repeatedly
with multiple argument sets. You can use this hook in modules and classes.
The decorator is btw implemented through a builtin pytest_generate_tests hook implementation and there can only be one per each module or class.
So let's now get to your original example at
https://github.com/tiddlyweb/tiddlyweb/blob/master/test/http_runner.py
The parametrize-relevant bits read:
def test_the_TESTS():
for test_data in tests:
test = dict(EMPTY_TEST)
test.update(test_data)
yield test['name'], _run_test, test
def _run_test(test):
...
I think you could directly rewrite it as:
def pytest_generate_tests(metafunc):
if metafunc.function == test_generic:
l = []
for test_data in tests:
test = dict(EMPTY_TEST)
test.update(test_data)
l.append(test)
metafunc.parametrize("test", argvalues=l, ids=ids)
def test_generic(test):
...
haven't tested this -- does it work for you?
best,
holger
More information about the pytest-dev
mailing list