From 16f472f7046a9817853e0f24609ede48a09d05e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malthe=20J=C3=B8rgensen?= Date: Wed, 26 Sep 2018 11:15:45 +0200 Subject: [PATCH] Add `--used-fixtures` command --- pytest_deadfixtures.py | 72 ++++++++++++++++++++++++++++++++++---- tests/test_deadfixtures.py | 36 +++++++++++++++++++ 2 files changed, 102 insertions(+), 6 deletions(-) diff --git a/pytest_deadfixtures.py b/pytest_deadfixtures.py index 7e323a6..f235cd0 100644 --- a/pytest_deadfixtures.py +++ b/pytest_deadfixtures.py @@ -15,6 +15,10 @@ "Hey there, I believe the following fixture(s) are not being used:" ) UNUSED_FIXTURES_NOT_FOUND_HEADLINE = "Cool, every declared fixture is being used." +USED_FIXTURES_FOUND_HEADLINE = ( + "Hey there, I believe the following fixture(s) are being used:" +) +USED_FIXTURES_NOT_FOUND_HEADLINE = "We could not find any fixtures being used" EXIT_CODE_ERROR = 11 EXIT_CODE_SUCCESS = 0 @@ -23,6 +27,8 @@ CachedFixture = namedtuple("CachedFixture", "fixturedef, relpath, result") +UsedFixture = namedtuple("UsedFixture", "relpath, argname, fixturedef") + def pytest_addoption(parser): group = parser.getgroup("deadfixtures") @@ -40,6 +46,13 @@ def pytest_addoption(parser): default=False, help="Show duplicated fixtures", ) + group.addoption( + "--used-fixtures", + action="store_true", + dest="usedfixtures", + default=False, + help="Show fixtures being used", + ) def pytest_cmdline_main(config): @@ -49,6 +62,10 @@ def pytest_cmdline_main(config): if _show_dead_fixtures(config): return EXIT_CODE_ERROR return EXIT_CODE_SUCCESS + elif config.option.usedfixtures: + if _show_used_fixtures(config): + return EXIT_CODE_ERROR + return EXIT_CODE_SUCCESS def _show_dead_fixtures(config): @@ -57,6 +74,12 @@ def _show_dead_fixtures(config): return wrap_session(config, show_dead_fixtures) +def _show_used_fixtures(config): + from _pytest.main import wrap_session + + return wrap_session(config, show_used_fixtures) + + def get_best_relpath(func, curdir): loc = getlocation(func, curdir) return curdir.bestrelpath(loc) @@ -98,8 +121,10 @@ def get_fixtures(session): return available -def get_used_fixturesdefs(session): - fixturesdefs = [] +def get_used_fixtures(session): + used = [] + seen = set() + curdir = py.path.local() for test_function in session.items: try: info = test_function._fixtureinfo @@ -113,8 +138,27 @@ def get_used_fixturesdefs(session): for _, fixturedefs in sorted(info.name2fixturedefs.items()): if fixturedefs is None: continue - fixturesdefs.append(fixturedefs[-1]) - return fixturesdefs + + for fixturedef in fixturedefs: + loc = getlocation(fixturedef.func, curdir) + if (fixturedef.argname, loc) in seen: + continue + + seen.add((fixturedef.argname, loc)) + + module = fixturedef.func.__module__ + + if ( + not module.startswith("_pytest.") + and not module.startswith("pytest_") + and not ("site-packages" in loc) + ): + used.append( + UsedFixture( + curdir.bestrelpath(loc), fixturedef.argname, fixturedef + ) + ) + return used def write_docstring(tw, doc): @@ -198,13 +242,13 @@ def show_dead_fixtures(config, session): tw = _pytest.config.create_terminal_writer(config) show_fixture_doc = config.getvalue("show_fixture_doc") - used_fixtures = get_used_fixturesdefs(session) + used_fixturedefs = [f.fixturedef for f in get_used_fixtures(session)] available_fixtures = get_fixtures(session) unused_fixtures = [ fixture for fixture in available_fixtures - if fixture.fixturedef not in used_fixtures + if fixture.fixturedef not in used_fixturedefs ] tw.line() @@ -214,3 +258,19 @@ def show_dead_fixtures(config, session): else: tw.line(UNUSED_FIXTURES_NOT_FOUND_HEADLINE, green=True) return unused_fixtures + + +def show_used_fixtures(config, session): + session.perform_collect() + tw = _pytest.config.create_terminal_writer(config) + verbose = config.getvalue("verbose") + + used_fixtures = get_used_fixtures(session) + + tw.line() + if used_fixtures: + tw.line(USED_FIXTURES_FOUND_HEADLINE, green=True) + write_fixtures(tw, used_fixtures, verbose) + else: + tw.line(USED_FIXTURES_NOT_FOUND_HEADLINE, red=True) + return used_fixtures diff --git a/tests/test_deadfixtures.py b/tests/test_deadfixtures.py index 71f07d5..c5f8775 100644 --- a/tests/test_deadfixtures.py +++ b/tests/test_deadfixtures.py @@ -66,6 +66,11 @@ def test_simple(): assert message not in result.stdout.str() + result = pytester.runpytest("--used-fixtures") + message = message_template.format("autouse_fixture", "test_dont_list_autouse_fixture") + + assert message in result.stdout.str() + def test_dont_list_same_file_fixture(pytester, message_template): pytester.makepyfile( @@ -90,6 +95,13 @@ def test_simple(same_file_fixture): assert message not in result.stdout.str() + result = pytester.runpytest("--used-fixtures") + message = message_template.format( + "same_file_fixture", "test_dont_list_same_file_fixture" + ) + + assert message in result.stdout.str() + def test_list_same_file_unused_fixture(pytester, message_template): pytester.makepyfile( @@ -114,6 +126,13 @@ def test_simple(): assert message in result.stdout.str() + result = pytester.runpytest("--used-fixtures") + message = message_template.format( + "same_file_fixture", "test_list_same_file_unused_fixture" + ) + + assert message not in result.stdout.str() + def test_list_same_file_multiple_unused_fixture(pytester, message_template): pytester.makepyfile( @@ -176,6 +195,11 @@ def test_conftest_fixture(conftest_fixture): assert message not in result.stdout.str() + result = pytester.runpytest("--used-fixtures") + message = message_template.format("conftest_fixture", "conftest") + + assert message in result.stdout.str() + def test_list_conftest_unused_fixture(pytester, message_template): pytester.makepyfile( @@ -204,6 +228,11 @@ def test_conftest_fixture(): assert message in result.stdout.str() + result = pytester.runpytest("--used-fixtures") + message = message_template.format("conftest_fixture", "conftest") + + assert message not in result.stdout.str() + def test_list_conftest_multiple_unused_fixture(pytester, message_template): pytester.makepyfile( @@ -266,6 +295,13 @@ def test_decorator_usefixtures(): assert message not in result.stdout.str() + result = pytester.runpytest("--used-fixtures") + message = message_template.format( + "decorator_usefixtures", "test_dont_list_decorator_usefixtures" + ) + + assert message in result.stdout.str() + def test_write_docs_when_verbose(pytester): pytester.makepyfile(