Recording Test Duration with Python pytest
Posted on Nov 20, 2016 in Python Module/Package Recommendations by Amo Chen ‐ 2 min read
Recently, I read an article titled Timing Tests in Python for Fun and Profit, which is well worth a read. The article discusses how to find test cases that need speed improvement by recording their execution time (and in the process, you might also discover code with poor performance).
However, I often use pytest, so I spent some extra time finding out how to achieve the same in pytest.
The environment used in this article is:
- Python 3.5
- pytest 3.0.4
The structure of the test folder is:
├── conftest.py
└── module_example
├── __init__.py
└── test_example.py
Actually, pytest already records the execution time for each test case, dividing it into three sections: setup, call, and teardown. We won’t delve into setup and teardown here; interested readers can go through the unittest documentation to learn about these terms. This post focuses directly on the call execution time, which is the actual time taken to run a test case.
By default, pytest doesn’t display the time for each test separately. To achieve this, you can use a pytest hook - pytest_terminal_summary. This hook can be used to retrieve a report of each test after all tests have completed and then print out their execution times individually.
The code is quite simple. You just need to implement the pytest_terminal_summary hook in conftest.py:
# conftest.py
def pytest_terminal_summary(terminalreporter, exitstatus):
# Gather each report
reports = []
for replist in terminalreporter.stats.values():
for rep in replist:
if hasattr(rep, 'duration'):
reports.append(rep)
if not reports:
return
# Sort by execution time (duration) in descending order
reports.sort(key=lambda x: x.duration)
reports.reverse()
terminalreporter.write_sep("=", "test durations")
for rep in reports:
# Focus only on the call stage
if rep.when != 'call':
continue
nodeid = rep.nodeid.replace("::()::", "::")
terminalreporter.write_line("%02.2fs %s" % (rep.duration, nodeid))
Execution result (the test durations section at the bottom):
▶ py.test .
============= test session starts =============
platform darwin -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0
rootdir: /home/example/tests, inifile:
collected 2 items
module_example/test_example.py ..
=============== test durations ================
5.00s module_example/test_example.py::test_abc
0.00s module_example/test_example.py::test_def
========== 2 passed in 5.02 seconds ===========
Additionally, to see what attributes are available in each report, check out TestReport.
That’s all.
Happy coding!
- http://doc.pytest.org/en/latest/_modules/_pytest/runner.html