Skip to content

Commit 308396a

Browse files
authored
Merge pull request #1606 from hackebrot/show-fixtures-per-test
Show fixtures per test
2 parents feeee28 + adc50ac commit 308396a

File tree

3 files changed

+213
-0
lines changed

3 files changed

+213
-0
lines changed

CHANGELOG.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@
3434
* New ``pytest_make_parametrize_id`` hook.
3535
Thanks `@palaviv`_ for the PR.
3636

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

3944
* Fixtures marked with ``@pytest.fixture`` can now use ``yield`` statements exactly like

_pytest/python.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,13 @@ def pytest_addoption(parser):
208208
group.addoption('--fixtures', '--funcargs',
209209
action="store_true", dest="showfixtures", default=False,
210210
help="show available fixtures, sorted by plugin appearance")
211+
group.addoption(
212+
'--fixtures-per-test',
213+
action="store_true",
214+
dest="show_fixtures_per_test",
215+
default=False,
216+
help="show fixtures per test",
217+
)
211218
parser.addini("usefixtures", type="args", default=[],
212219
help="list of default fixtures to be used with this project")
213220
parser.addini("python_files", type="args",
@@ -229,6 +236,9 @@ def pytest_cmdline_main(config):
229236
if config.option.showfixtures:
230237
showfixtures(config)
231238
return 0
239+
if config.option.show_fixtures_per_test:
240+
show_fixtures_per_test(config)
241+
return 0
232242

233243

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

1207+
1208+
def show_fixtures_per_test(config):
1209+
from _pytest.main import wrap_session
1210+
return wrap_session(config, _show_fixtures_per_test)
1211+
1212+
1213+
def _show_fixtures_per_test(config, session):
1214+
import _pytest.config
1215+
session.perform_collect()
1216+
curdir = py.path.local()
1217+
tw = _pytest.config.create_terminal_writer(config)
1218+
verbose = config.getvalue("verbose")
1219+
1220+
def get_best_rel(func):
1221+
loc = getlocation(func, curdir)
1222+
return curdir.bestrelpath(loc)
1223+
1224+
def write_fixture(fixture_def):
1225+
argname = fixture_def.argname
1226+
1227+
if verbose <= 0 and argname.startswith("_"):
1228+
return
1229+
if verbose > 0:
1230+
bestrel = get_best_rel(fixture_def.func)
1231+
funcargspec = "{0} -- {1}".format(argname, bestrel)
1232+
else:
1233+
funcargspec = argname
1234+
tw.line(funcargspec, green=True)
1235+
1236+
INDENT = ' {0}'
1237+
fixture_doc = fixture_def.func.__doc__
1238+
1239+
if fixture_doc:
1240+
for line in fixture_doc.strip().split('\n'):
1241+
tw.line(INDENT.format(line.strip()))
1242+
else:
1243+
tw.line(INDENT.format('no docstring available'), red=True)
1244+
1245+
def write_item(item):
1246+
name2fixturedefs = item._fixtureinfo.name2fixturedefs
1247+
1248+
if not name2fixturedefs:
1249+
# The given test item does not use any fixtures
1250+
return
1251+
bestrel = get_best_rel(item.function)
1252+
1253+
tw.line()
1254+
tw.sep('-', 'fixtures used by {0}'.format(item.name))
1255+
tw.sep('-', '({0})'.format(bestrel))
1256+
for argname, fixture_defs in sorted(name2fixturedefs.items()):
1257+
assert fixture_defs is not None
1258+
if not fixture_defs:
1259+
continue
1260+
# The last fixture def item in the list is expected
1261+
# to be the one used by the test item
1262+
write_fixture(fixture_defs[-1])
1263+
1264+
for item in session.items:
1265+
write_item(item)
1266+
1267+
11971268
def showfixtures(config):
11981269
from _pytest.main import wrap_session
11991270
return wrap_session(config, _showfixtures_main)
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# -*- coding: utf-8 -*-
2+
3+
4+
def test_no_items_should_not_show_output(testdir):
5+
result = testdir.runpytest('--fixtures-per-test')
6+
assert 'fixtures used by' not in result.stdout.str()
7+
assert result.ret == 0
8+
9+
10+
def test_fixtures_in_module(testdir):
11+
p = testdir.makepyfile('''
12+
import pytest
13+
@pytest.fixture
14+
def _arg0():
15+
"""hidden arg0 fixture"""
16+
@pytest.fixture
17+
def arg1():
18+
"""arg1 docstring"""
19+
def test_arg1(arg1):
20+
pass
21+
''')
22+
23+
result = testdir.runpytest("--fixtures-per-test", p)
24+
assert result.ret == 0
25+
26+
result.stdout.fnmatch_lines([
27+
'*fixtures used by test_arg1*',
28+
'*(test_fixtures_in_module.py:9)*',
29+
'arg1',
30+
' arg1 docstring',
31+
])
32+
assert "_arg0" not in result.stdout.str()
33+
34+
35+
def test_fixtures_in_conftest(testdir):
36+
testdir.makeconftest('''
37+
import pytest
38+
@pytest.fixture
39+
def arg1():
40+
"""arg1 docstring"""
41+
@pytest.fixture
42+
def arg2():
43+
"""arg2 docstring"""
44+
@pytest.fixture
45+
def arg3(arg1, arg2):
46+
"""arg3
47+
docstring
48+
"""
49+
''')
50+
p = testdir.makepyfile('''
51+
def test_arg2(arg2):
52+
pass
53+
def test_arg3(arg3):
54+
pass
55+
''')
56+
result = testdir.runpytest("--fixtures-per-test", p)
57+
assert result.ret == 0
58+
59+
result.stdout.fnmatch_lines([
60+
'*fixtures used by test_arg2*',
61+
'*(test_fixtures_in_conftest.py:2)*',
62+
'arg2',
63+
' arg2 docstring',
64+
'*fixtures used by test_arg3*',
65+
'*(test_fixtures_in_conftest.py:4)*',
66+
'arg1',
67+
' arg1 docstring',
68+
'arg2',
69+
' arg2 docstring',
70+
'arg3',
71+
' arg3',
72+
' docstring',
73+
])
74+
75+
76+
def test_should_show_fixtures_used_by_test(testdir):
77+
testdir.makeconftest('''
78+
import pytest
79+
@pytest.fixture
80+
def arg1():
81+
"""arg1 from conftest"""
82+
@pytest.fixture
83+
def arg2():
84+
"""arg2 from conftest"""
85+
''')
86+
p = testdir.makepyfile('''
87+
import pytest
88+
@pytest.fixture
89+
def arg1():
90+
"""arg1 from testmodule"""
91+
def test_args(arg1, arg2):
92+
pass
93+
''')
94+
result = testdir.runpytest("--fixtures-per-test", p)
95+
assert result.ret == 0
96+
97+
result.stdout.fnmatch_lines([
98+
'*fixtures used by test_args*',
99+
'*(test_should_show_fixtures_used_by_test.py:6)*',
100+
'arg1',
101+
' arg1 from testmodule',
102+
'arg2',
103+
' arg2 from conftest',
104+
])
105+
106+
107+
def test_verbose_include_private_fixtures_and_loc(testdir):
108+
testdir.makeconftest('''
109+
import pytest
110+
@pytest.fixture
111+
def _arg1():
112+
"""_arg1 from conftest"""
113+
@pytest.fixture
114+
def arg2(_arg1):
115+
"""arg2 from conftest"""
116+
''')
117+
p = testdir.makepyfile('''
118+
import pytest
119+
@pytest.fixture
120+
def arg3():
121+
"""arg3 from testmodule"""
122+
def test_args(arg2, arg3):
123+
pass
124+
''')
125+
result = testdir.runpytest("--fixtures-per-test", "-v", p)
126+
assert result.ret == 0
127+
128+
result.stdout.fnmatch_lines([
129+
'*fixtures used by test_args*',
130+
'*(test_verbose_include_private_fixtures_and_loc.py:6)*',
131+
'_arg1 -- conftest.py:3',
132+
' _arg1 from conftest',
133+
'arg2 -- conftest.py:6',
134+
' arg2 from conftest',
135+
'arg3 -- test_verbose_include_private_fixtures_and_loc.py:3',
136+
' arg3 from testmodule',
137+
])

0 commit comments

Comments
 (0)