pytest
Table of Contents
Overview
Commandline Usage
pytest --version # shows where pytest was imported from
pytest --fixtures # show available builtin function arguments
pytest -h | --help # show help on command line and config file options
pytest -x # stop after first failure
pytest --maxfail=2 # stop after two failures
pytest test_mod.py # run tests in module
pytest somepath # run all tests below somepath
pytest --capture=fd # OS level; 1,2 fds are captured
pytest --capture=sys # sys module level; sys.stdout, sys.stderr are captured
pytest --capture=no # nothing captured
pytest -s # same as --capture=no
# only run tests with names that match the # "string expression".
# e.g. "MyClass and not method" # will select
# TestMyClass.test_something but not TestMyClass.test_method_simple
pytest -k stringexpr
# only run tests that match the "node ID",
pytest test_mod.py::test_func
pytest test_mod.py::TestClass::test_method
pytest --pyargs pkg # run all tests found below directory of pkg
pytest --showlocals # show local variables in tracebacks
pytest -l # show local variables (shortcut)
pytest --tb=auto # (default) 'long' tracebacks for the first and last
# entry, but 'short' style for the other entries
pytest --tb=long # exhaustive, informative traceback formatting
pytest --tb=short # shorter traceback format
pytest --tb=line # only one line per failure
pytest --tb=native # Python standard library formatting
pytest --tb=no # no traceback at all
pytest --pdb # drop into the PDB prompt
pytest --durations=10 # To get a list of the slowest 10 test durations
Reference
approx
>>> from pytest import approx
>>> 0.1 + 0.2 == approx(0.3)
True
>>> (0.1 + 0.2, 0.2 + 0.4) == approx((0.3, 0.6))
True
fixture
- https://docs.pytest.org/en/latest/fixture.html
- https://docs.pytest.org/en/latest/builtin.html#_pytest.fixtures.fixture
- https://docs.pytest.org/en/latest/builtin.html#builtin-fixtures-function-arguments
- https://docs.pytest.org/en/latest/fixture.html#higher-scoped-fixtures-are-instantiated-first
- https://docs.pytest.org/en/latest/fixture.html#usefixtures
scope
session
module
function
(default)
# content of ./test_smtpsimple.py
import pytest
@pytest.fixture
def smtp():
import smtplib
return smtplib.SMTP("smtp.gmail.com")
def test_ehlo(smtp):
response, msg = smtp.ehlo()
assert response == 250
assert 0 # for demo purposes
There are several ways to use a fixture:
# Specify it as a function argument
@pytest.fixture
def foo():
pass
def test_bar(foo):
pass
# Only wants the fixture's effects
@pytest.mark.usefixtures('foo')
def test_baz():
pass
# autouse=True for 'module' or 'session' scope
@pytest.fixture(scope='session', autouse=True)
def foo():
pass
@pytest.fixture(scope="module") # only be invoked once per test module
def smtp():
smtp = smtplib.SMTP("smtp.gmail.com")
yield smtp # provide the fixture value
print("teardown smtp")
smtp.close()
# request example
@pytest.fixture(scope="module")
def smtp(request):
server = getattr(request.module, "smtpserver", "smtp.gmail.com")
smtp = smtplib.SMTP(server)
yield smtp
print ("finalizing %s (%s)" % (smtp, server))
smtp.close()
# Each test case will be executed for
# each fixture based on each element of params
@pytest.fixture(scope="module",
params=["smtp.gmail.com", "mail.python.org"])
def smtp(request):
smtp = smtplib.SMTP(request.param)
yield smtp
print ("finalizing %s" % smtp)
smtp.close()
# Set ids to inhence the readability of tests
@pytest.fixture(params=[0, 1], ids=["spam", "ham"])
def a(request):
return request.param
def test_a(a):
pass
# pytest prints out like:
# <Function 'test_a[spam]'>
# <Function 'test_a[ham]'>
pytest minimizes the number of active fixtures during test runs. If you have a parametrized fixture, then all the tests using it will first execute with one instance and then finalizers are called before the next fixture instance is created.
For example:
- You define a fixture named
a
- You define fixtures of
b
andc
depending ona
- You define a test
test_x
uses fixtures ofb
andc
In this case, the fixture a
is called only once for each run of test_x
@pytest.fixture
def fruit():
pass
@pytest.fixture
def apple(fruit):
pass
@pytest.fixture
def banana(fruit):
pass
def test_foo(apple, banana):
# For this test, 'fruit' is executed once.
# 'apple' and 'banana' uses the same 'fruit'
pass
mark
import pytest
@pytest.mark.parametrize("test_input,expected", [
("3+5", 8),
("2+4", 6),
("6*9", 42),
])
def test_eval(test_input, expected):
assert eval(test_input) == expected
monkeypatch
monkeypatch.setattr/delattr/delitem/delenv()
all by default raise an Exception if the target does not exist. Passraising=False
if you want to skip this check.
import module
def test_monkeypatch(monkeypatch):
monkeypatch.setattr(module, 'name', 'value')
monkeypatch.setattr('module.name', 'value') # same as above
monkeypatch.delattr('module.name')
d = {}
monkeypatch.setitem(d, 'key', 'value')
monkeypatch.delitem(d, 'key')
# environment variable
monkeypatch.setenv('FOO', 'VALUE')
monkeypatch.delenv('FOO')
monkeypatch.syspath_prepend('./bin')
monkeypatch.chdir('../')
monkeypatch.undo() # undo all changes
raises
import pytest
def test_zero_division():
with pytest.raises(ZeroDivisionError):
1 / 0
def test_recursion_depth():
with pytest.raises(RuntimeError) as exc_info:
def f():
f()
f()
assert exc_info.match('maximum recursion' )
fail
Topics
rootdir
The rootdir is used a reference directory for constructing test addresses (“nodeids”) and can be used also by plugins for storing per-testrun information.
Import Mechanisms
Test Layouts
Useful if you have many functional tests or for other reasons want to keep tests separate from actual application code (often a good idea):
setup.py # your setuptools Python package metadata
mypkg/
__init__.py
appmodule.py
tests/
test_app.py
...
Useful if you have direct relation between (unit-)test and application modules and want to distribute your tests along with your application:
setup.py # your setuptools Python package metadata
mypkg/
__init__.py
appmodule.py
...
test/
test_app.py
...
pytest tests/test_app.py # for external test dirs
pytest mypkg/test/test_app.py # for inlined test dirs
pytest mypkg # run tests in all below test directories
pytest # run all tests below current dir