Skip to content

Commit

Permalink
Merge pull request #1606 from hackebrot/show-fixtures-per-test
Browse files Browse the repository at this point in the history
Show fixtures per test
  • Loading branch information
nicoddemus authored Jun 14, 2016
2 parents feeee28 + adc50ac commit 308396a
Show file tree
Hide file tree
Showing 3 changed files with 213 additions and 0 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
* New ``pytest_make_parametrize_id`` hook.
Thanks `@palaviv`_ for the PR.

* New cli flag ``--fixtures-per-test`` that shows which fixtures are being used
for each selected test item. Features doc strings of fixtures by default.
Can also show where fixtures are defined if combined with ``-v``.
Thanks `@hackebrot`_ for the PR.

**Changes**

* Fixtures marked with ``@pytest.fixture`` can now use ``yield`` statements exactly like
Expand Down
71 changes: 71 additions & 0 deletions _pytest/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,13 @@ def pytest_addoption(parser):
group.addoption('--fixtures', '--funcargs',
action="store_true", dest="showfixtures", default=False,
help="show available fixtures, sorted by plugin appearance")
group.addoption(
'--fixtures-per-test',
action="store_true",
dest="show_fixtures_per_test",
default=False,
help="show fixtures per test",
)
parser.addini("usefixtures", type="args", default=[],
help="list of default fixtures to be used with this project")
parser.addini("python_files", type="args",
Expand All @@ -229,6 +236,9 @@ def pytest_cmdline_main(config):
if config.option.showfixtures:
showfixtures(config)
return 0
if config.option.show_fixtures_per_test:
show_fixtures_per_test(config)
return 0


def pytest_generate_tests(metafunc):
Expand Down Expand Up @@ -1194,6 +1204,67 @@ def idmaker(argnames, argvalues, idfn=None, ids=None, config=None):
counters[testid] += 1
return ids


def show_fixtures_per_test(config):
from _pytest.main import wrap_session
return wrap_session(config, _show_fixtures_per_test)


def _show_fixtures_per_test(config, session):
import _pytest.config
session.perform_collect()
curdir = py.path.local()
tw = _pytest.config.create_terminal_writer(config)
verbose = config.getvalue("verbose")

def get_best_rel(func):
loc = getlocation(func, curdir)
return curdir.bestrelpath(loc)

def write_fixture(fixture_def):
argname = fixture_def.argname

if verbose <= 0 and argname.startswith("_"):
return
if verbose > 0:
bestrel = get_best_rel(fixture_def.func)
funcargspec = "{0} -- {1}".format(argname, bestrel)
else:
funcargspec = argname
tw.line(funcargspec, green=True)

INDENT = ' {0}'
fixture_doc = fixture_def.func.__doc__

if fixture_doc:
for line in fixture_doc.strip().split('\n'):
tw.line(INDENT.format(line.strip()))
else:
tw.line(INDENT.format('no docstring available'), red=True)

def write_item(item):
name2fixturedefs = item._fixtureinfo.name2fixturedefs

if not name2fixturedefs:
# The given test item does not use any fixtures
return
bestrel = get_best_rel(item.function)

tw.line()
tw.sep('-', 'fixtures used by {0}'.format(item.name))
tw.sep('-', '({0})'.format(bestrel))
for argname, fixture_defs in sorted(name2fixturedefs.items()):
assert fixture_defs is not None
if not fixture_defs:
continue
# The last fixture def item in the list is expected
# to be the one used by the test item
write_fixture(fixture_defs[-1])

for item in session.items:
write_item(item)


def showfixtures(config):
from _pytest.main import wrap_session
return wrap_session(config, _showfixtures_main)
Expand Down
137 changes: 137 additions & 0 deletions testing/python/show_fixtures_per_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# -*- coding: utf-8 -*-


def test_no_items_should_not_show_output(testdir):
result = testdir.runpytest('--fixtures-per-test')
assert 'fixtures used by' not in result.stdout.str()
assert result.ret == 0


def test_fixtures_in_module(testdir):
p = testdir.makepyfile('''
import pytest
@pytest.fixture
def _arg0():
"""hidden arg0 fixture"""
@pytest.fixture
def arg1():
"""arg1 docstring"""
def test_arg1(arg1):
pass
''')

result = testdir.runpytest("--fixtures-per-test", p)
assert result.ret == 0

result.stdout.fnmatch_lines([
'*fixtures used by test_arg1*',
'*(test_fixtures_in_module.py:9)*',
'arg1',
' arg1 docstring',
])
assert "_arg0" not in result.stdout.str()


def test_fixtures_in_conftest(testdir):
testdir.makeconftest('''
import pytest
@pytest.fixture
def arg1():
"""arg1 docstring"""
@pytest.fixture
def arg2():
"""arg2 docstring"""
@pytest.fixture
def arg3(arg1, arg2):
"""arg3
docstring
"""
''')
p = testdir.makepyfile('''
def test_arg2(arg2):
pass
def test_arg3(arg3):
pass
''')
result = testdir.runpytest("--fixtures-per-test", p)
assert result.ret == 0

result.stdout.fnmatch_lines([
'*fixtures used by test_arg2*',
'*(test_fixtures_in_conftest.py:2)*',
'arg2',
' arg2 docstring',
'*fixtures used by test_arg3*',
'*(test_fixtures_in_conftest.py:4)*',
'arg1',
' arg1 docstring',
'arg2',
' arg2 docstring',
'arg3',
' arg3',
' docstring',
])


def test_should_show_fixtures_used_by_test(testdir):
testdir.makeconftest('''
import pytest
@pytest.fixture
def arg1():
"""arg1 from conftest"""
@pytest.fixture
def arg2():
"""arg2 from conftest"""
''')
p = testdir.makepyfile('''
import pytest
@pytest.fixture
def arg1():
"""arg1 from testmodule"""
def test_args(arg1, arg2):
pass
''')
result = testdir.runpytest("--fixtures-per-test", p)
assert result.ret == 0

result.stdout.fnmatch_lines([
'*fixtures used by test_args*',
'*(test_should_show_fixtures_used_by_test.py:6)*',
'arg1',
' arg1 from testmodule',
'arg2',
' arg2 from conftest',
])


def test_verbose_include_private_fixtures_and_loc(testdir):
testdir.makeconftest('''
import pytest
@pytest.fixture
def _arg1():
"""_arg1 from conftest"""
@pytest.fixture
def arg2(_arg1):
"""arg2 from conftest"""
''')
p = testdir.makepyfile('''
import pytest
@pytest.fixture
def arg3():
"""arg3 from testmodule"""
def test_args(arg2, arg3):
pass
''')
result = testdir.runpytest("--fixtures-per-test", "-v", p)
assert result.ret == 0

result.stdout.fnmatch_lines([
'*fixtures used by test_args*',
'*(test_verbose_include_private_fixtures_and_loc.py:6)*',
'_arg1 -- conftest.py:3',
' _arg1 from conftest',
'arg2 -- conftest.py:6',
' arg2 from conftest',
'arg3 -- test_verbose_include_private_fixtures_and_loc.py:3',
' arg3 from testmodule',
])

0 comments on commit 308396a

Please sign in to comment.