diff --git a/_pytest/hookspec.py b/_pytest/hookspec.py index ec4659159..32116bc08 100644 --- a/_pytest/hookspec.py +++ b/_pytest/hookspec.py @@ -304,7 +304,25 @@ def pytest_runtest_protocol(item, nextitem): def pytest_runtest_logstart(nodeid, location): - """ signal the start of running a single test item. """ + """ signal the start of running a single test item. + + This hook will be called **before** :func:`pytest_runtest_setup`, :func:`pytest_runtest_call` and + :func:`pytest_runtest_teardown` hooks. + + :param str nodeid: full id of the item + :param location: a triple of ``(filename, linenum, testname)`` + """ + + +def pytest_runtest_logfinish(nodeid, location): + """ signal the complete finish of running a single test item. + + This hook will be called **after** :func:`pytest_runtest_setup`, :func:`pytest_runtest_call` and + :func:`pytest_runtest_teardown` hooks. + + :param str nodeid: full id of the item + :param location: a triple of ``(filename, linenum, testname)`` + """ def pytest_runtest_setup(item): @@ -445,7 +463,7 @@ def pytest_terminal_summary(terminalreporter, exitstatus): def pytest_logwarning(message, code, nodeid, fslocation): """ process a warning specified by a message, a code string, a nodeid and fslocation (both of which may be None - if the warning is not tied to a partilar node/location).""" + if the warning is not tied to a particular node/location).""" # ------------------------------------------------------------------------- # doctest hooks diff --git a/_pytest/runner.py b/_pytest/runner.py index e07ed2a24..13abee367 100644 --- a/_pytest/runner.py +++ b/_pytest/runner.py @@ -60,6 +60,9 @@ def pytest_runtest_protocol(item, nextitem): nodeid=item.nodeid, location=item.location, ) runtestprotocol(item, nextitem=nextitem) + item.ihook.pytest_runtest_logfinish( + nodeid=item.nodeid, location=item.location, + ) return True diff --git a/changelog/3101.feature b/changelog/3101.feature new file mode 100644 index 000000000..1ed0a8e08 --- /dev/null +++ b/changelog/3101.feature @@ -0,0 +1,3 @@ +New `pytest_runtest_logfinish `_ +hook which is called when a test item has finished executing, analogous to +`pytest_runtest_logstart `_. diff --git a/doc/en/writing_plugins.rst b/doc/en/writing_plugins.rst index 9a74d6b4e..55765d22d 100644 --- a/doc/en/writing_plugins.rst +++ b/doc/en/writing_plugins.rst @@ -598,6 +598,8 @@ All runtest related hooks receive a :py:class:`pytest.Item <_pytest.main.Item>` .. autofunction:: pytest_runtestloop .. autofunction:: pytest_runtest_protocol +.. autofunction:: pytest_runtest_logstart +.. autofunction:: pytest_runtest_logfinish .. autofunction:: pytest_runtest_setup .. autofunction:: pytest_runtest_call .. autofunction:: pytest_runtest_teardown diff --git a/testing/test_runner.py b/testing/test_runner.py index a8dc8dfbd..48e707661 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -202,6 +202,18 @@ class BaseFunctionalTests(object): """) assert rec.ret == 1 + def test_logstart_logfinish_hooks(self, testdir): + rec = testdir.inline_runsource(""" + import pytest + def test_func(): + pass + """) + reps = rec.getcalls("pytest_runtest_logstart pytest_runtest_logfinish") + assert [x._name for x in reps] == ['pytest_runtest_logstart', 'pytest_runtest_logfinish'] + for rep in reps: + assert rep.nodeid == 'test_logstart_logfinish_hooks.py::test_func' + assert rep.location == ('test_logstart_logfinish_hooks.py', 1, 'test_func') + def test_exact_teardown_issue90(self, testdir): rec = testdir.inline_runsource(""" import pytest