ran blacken-docs
This commit is contained in:
parent
75d0b899bb
commit
5f95dce956
|
@ -238,7 +238,10 @@ file which provides an alternative explanation for ``Foo`` objects:
|
||||||
|
|
||||||
def pytest_assertrepr_compare(op, left, right):
|
def pytest_assertrepr_compare(op, left, right):
|
||||||
if isinstance(left, Foo) and isinstance(right, Foo) and op == "==":
|
if isinstance(left, Foo) and isinstance(right, Foo) and op == "==":
|
||||||
return ["Comparing Foo instances:", " vals: {} != {}".format(left.val, right.val)]
|
return [
|
||||||
|
"Comparing Foo instances:",
|
||||||
|
" vals: {} != {}".format(left.val, right.val),
|
||||||
|
]
|
||||||
|
|
||||||
now, given this test module:
|
now, given this test module:
|
||||||
|
|
||||||
|
|
|
@ -167,4 +167,5 @@ You can also interactively ask for help, e.g. by typing on the Python interactiv
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
help(pytest)
|
help(pytest)
|
||||||
|
|
|
@ -40,10 +40,11 @@ First, let's create 50 test invocation of which only 2 fail::
|
||||||
# content of test_50.py
|
# content of test_50.py
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("i", range(50))
|
@pytest.mark.parametrize("i", range(50))
|
||||||
def test_num(i):
|
def test_num(i):
|
||||||
if i in (17, 25):
|
if i in (17, 25):
|
||||||
pytest.fail("bad luck")
|
pytest.fail("bad luck")
|
||||||
|
|
||||||
If you run this for the first time you will see two failures:
|
If you run this for the first time you will see two failures:
|
||||||
|
|
||||||
|
@ -193,9 +194,11 @@ across pytest invocations::
|
||||||
import pytest
|
import pytest
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
|
||||||
def expensive_computation():
|
def expensive_computation():
|
||||||
print("running expensive computation...")
|
print("running expensive computation...")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mydata(request):
|
def mydata(request):
|
||||||
val = request.config.cache.get("example/value", None)
|
val = request.config.cache.get("example/value", None)
|
||||||
|
@ -205,6 +208,7 @@ across pytest invocations::
|
||||||
request.config.cache.set("example/value", val)
|
request.config.cache.set("example/value", val)
|
||||||
return val
|
return val
|
||||||
|
|
||||||
|
|
||||||
def test_function(mydata):
|
def test_function(mydata):
|
||||||
assert mydata == 23
|
assert mydata == 23
|
||||||
|
|
||||||
|
|
|
@ -55,12 +55,15 @@ is that you can use print statements for debugging::
|
||||||
|
|
||||||
# content of test_module.py
|
# content of test_module.py
|
||||||
|
|
||||||
|
|
||||||
def setup_function(function):
|
def setup_function(function):
|
||||||
print("setting up %s" % function)
|
print("setting up %s" % function)
|
||||||
|
|
||||||
|
|
||||||
def test_func1():
|
def test_func1():
|
||||||
assert True
|
assert True
|
||||||
|
|
||||||
|
|
||||||
def test_func2():
|
def test_func2():
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
|
|
|
@ -197,9 +197,11 @@ place the objects you want to appear in the doctest namespace::
|
||||||
|
|
||||||
# content of conftest.py
|
# content of conftest.py
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def add_np(doctest_namespace):
|
def add_np(doctest_namespace):
|
||||||
doctest_namespace['np'] = numpy
|
doctest_namespace["np"] = numpy
|
||||||
|
|
||||||
which can then be used in your doctests directly::
|
which can then be used in your doctests directly::
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ line argument. Let's first write a simple (do-nothing) computation test::
|
||||||
|
|
||||||
# content of test_compute.py
|
# content of test_compute.py
|
||||||
|
|
||||||
|
|
||||||
def test_compute(param1):
|
def test_compute(param1):
|
||||||
assert param1 < 4
|
assert param1 < 4
|
||||||
|
|
||||||
|
@ -34,13 +35,14 @@ Now we add a test configuration like this::
|
||||||
|
|
||||||
# content of conftest.py
|
# content of conftest.py
|
||||||
|
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
parser.addoption("--all", action="store_true",
|
parser.addoption("--all", action="store_true", help="run all combinations")
|
||||||
help="run all combinations")
|
|
||||||
|
|
||||||
def pytest_generate_tests(metafunc):
|
def pytest_generate_tests(metafunc):
|
||||||
if 'param1' in metafunc.fixturenames:
|
if "param1" in metafunc.fixturenames:
|
||||||
if metafunc.config.getoption('all'):
|
if metafunc.config.getoption("all"):
|
||||||
end = 5
|
end = 5
|
||||||
else:
|
else:
|
||||||
end = 2
|
end = 2
|
||||||
|
@ -118,7 +120,7 @@ the argument name::
|
||||||
def idfn(val):
|
def idfn(val):
|
||||||
if isinstance(val, (datetime,)):
|
if isinstance(val, (datetime,)):
|
||||||
# note this wouldn't show any hours/minutes/seconds
|
# note this wouldn't show any hours/minutes/seconds
|
||||||
return val.strftime('%Y%m%d')
|
return val.strftime("%Y%m%d")
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("a,b,expected", testdata, ids=idfn)
|
@pytest.mark.parametrize("a,b,expected", testdata, ids=idfn)
|
||||||
|
@ -126,12 +128,18 @@ the argument name::
|
||||||
diff = a - b
|
diff = a - b
|
||||||
assert diff == expected
|
assert diff == expected
|
||||||
|
|
||||||
@pytest.mark.parametrize("a,b,expected", [
|
|
||||||
pytest.param(datetime(2001, 12, 12), datetime(2001, 12, 11),
|
@pytest.mark.parametrize(
|
||||||
timedelta(1), id='forward'),
|
"a,b,expected",
|
||||||
pytest.param(datetime(2001, 12, 11), datetime(2001, 12, 12),
|
[
|
||||||
timedelta(-1), id='backward'),
|
pytest.param(
|
||||||
])
|
datetime(2001, 12, 12), datetime(2001, 12, 11), timedelta(1), id="forward"
|
||||||
|
),
|
||||||
|
pytest.param(
|
||||||
|
datetime(2001, 12, 11), datetime(2001, 12, 12), timedelta(-1), id="backward"
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
def test_timedistance_v3(a, b, expected):
|
def test_timedistance_v3(a, b, expected):
|
||||||
diff = a - b
|
diff = a - b
|
||||||
assert diff == expected
|
assert diff == expected
|
||||||
|
@ -183,6 +191,7 @@ only have to work a bit to construct the correct arguments for pytest's
|
||||||
|
|
||||||
# content of test_scenarios.py
|
# content of test_scenarios.py
|
||||||
|
|
||||||
|
|
||||||
def pytest_generate_tests(metafunc):
|
def pytest_generate_tests(metafunc):
|
||||||
idlist = []
|
idlist = []
|
||||||
argvalues = []
|
argvalues = []
|
||||||
|
@ -193,8 +202,10 @@ only have to work a bit to construct the correct arguments for pytest's
|
||||||
argvalues.append([x[1] for x in items])
|
argvalues.append([x[1] for x in items])
|
||||||
metafunc.parametrize(argnames, argvalues, ids=idlist, scope="class")
|
metafunc.parametrize(argnames, argvalues, ids=idlist, scope="class")
|
||||||
|
|
||||||
scenario1 = ('basic', {'attribute': 'value'})
|
|
||||||
scenario2 = ('advanced', {'attribute': 'value2'})
|
scenario1 = ("basic", {"attribute": "value"})
|
||||||
|
scenario2 = ("advanced", {"attribute": "value2"})
|
||||||
|
|
||||||
|
|
||||||
class TestSampleWithScenarios:
|
class TestSampleWithScenarios:
|
||||||
scenarios = [scenario1, scenario2]
|
scenarios = [scenario1, scenario2]
|
||||||
|
@ -259,6 +270,8 @@ the actual test requiring a ``db`` object::
|
||||||
# content of test_backends.py
|
# content of test_backends.py
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
def test_db_initialized(db):
|
def test_db_initialized(db):
|
||||||
# a dummy test
|
# a dummy test
|
||||||
if db.__class__.__name__ == "DB2":
|
if db.__class__.__name__ == "DB2":
|
||||||
|
@ -273,15 +286,20 @@ creates a database object for the actual test invocations::
|
||||||
# content of conftest.py
|
# content of conftest.py
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
def pytest_generate_tests(metafunc):
|
def pytest_generate_tests(metafunc):
|
||||||
if 'db' in metafunc.fixturenames:
|
if "db" in metafunc.fixturenames:
|
||||||
metafunc.parametrize("db", ['d1', 'd2'], indirect=True)
|
metafunc.parametrize("db", ["d1", "d2"], indirect=True)
|
||||||
|
|
||||||
|
|
||||||
class DB1:
|
class DB1:
|
||||||
"one database object"
|
"one database object"
|
||||||
|
|
||||||
|
|
||||||
class DB2:
|
class DB2:
|
||||||
"alternative database object"
|
"alternative database object"
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def db(request):
|
def db(request):
|
||||||
if request.param == "d1":
|
if request.param == "d1":
|
||||||
|
@ -346,18 +364,22 @@ will be passed to respective fixture function::
|
||||||
# content of test_indirect_list.py
|
# content of test_indirect_list.py
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.fixture(scope='function')
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="function")
|
||||||
def x(request):
|
def x(request):
|
||||||
return request.param * 3
|
return request.param * 3
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
|
||||||
|
@pytest.fixture(scope="function")
|
||||||
def y(request):
|
def y(request):
|
||||||
return request.param * 2
|
return request.param * 2
|
||||||
|
|
||||||
@pytest.mark.parametrize('x, y', [('a', 'b')], indirect=['x'])
|
|
||||||
def test_indirect(x,y):
|
@pytest.mark.parametrize("x, y", [("a", "b")], indirect=["x"])
|
||||||
assert x == 'aaa'
|
def test_indirect(x, y):
|
||||||
assert y == 'b'
|
assert x == "aaa"
|
||||||
|
assert y == "b"
|
||||||
|
|
||||||
The result of this test will be successful:
|
The result of this test will be successful:
|
||||||
|
|
||||||
|
@ -391,18 +413,21 @@ parametrizer`_ but in a lot less code::
|
||||||
# content of ./test_parametrize.py
|
# content of ./test_parametrize.py
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
def pytest_generate_tests(metafunc):
|
def pytest_generate_tests(metafunc):
|
||||||
# called once per each test function
|
# called once per each test function
|
||||||
funcarglist = metafunc.cls.params[metafunc.function.__name__]
|
funcarglist = metafunc.cls.params[metafunc.function.__name__]
|
||||||
argnames = sorted(funcarglist[0])
|
argnames = sorted(funcarglist[0])
|
||||||
metafunc.parametrize(argnames, [[funcargs[name] for name in argnames]
|
metafunc.parametrize(
|
||||||
for funcargs in funcarglist])
|
argnames, [[funcargs[name] for name in argnames] for funcargs in funcarglist]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestClass:
|
class TestClass:
|
||||||
# a map specifying multiple argument sets for a test method
|
# a map specifying multiple argument sets for a test method
|
||||||
params = {
|
params = {
|
||||||
'test_equals': [dict(a=1, b=2), dict(a=3, b=3), ],
|
"test_equals": [dict(a=1, b=2), dict(a=3, b=3)],
|
||||||
'test_zerodivision': [dict(a=1, b=0), ],
|
"test_zerodivision": [dict(a=1, b=0)],
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_equals(self, a, b):
|
def test_equals(self, a, b):
|
||||||
|
@ -471,10 +496,12 @@ need to provide similar results::
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def basemod(request):
|
def basemod(request):
|
||||||
return pytest.importorskip("base")
|
return pytest.importorskip("base")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session", params=["opt1", "opt2"])
|
@pytest.fixture(scope="session", params=["opt1", "opt2"])
|
||||||
def optmod(request):
|
def optmod(request):
|
||||||
return pytest.importorskip(request.param)
|
return pytest.importorskip(request.param)
|
||||||
|
@ -501,6 +528,7 @@ And finally a little test module::
|
||||||
|
|
||||||
# content of test_module.py
|
# content of test_module.py
|
||||||
|
|
||||||
|
|
||||||
def test_func1(basemod, optmod):
|
def test_func1(basemod, optmod):
|
||||||
assert round(basemod.func1(), 3) == round(optmod.func1(), 3)
|
assert round(basemod.func1(), 3) == round(optmod.func1(), 3)
|
||||||
|
|
||||||
|
@ -610,17 +638,21 @@ as a complement to ``raises``. For example::
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def does_not_raise():
|
def does_not_raise():
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('example_input,expectation', [
|
@pytest.mark.parametrize(
|
||||||
(3, does_not_raise()),
|
"example_input,expectation",
|
||||||
(2, does_not_raise()),
|
[
|
||||||
(1, does_not_raise()),
|
(3, does_not_raise()),
|
||||||
(0, pytest.raises(ZeroDivisionError)),
|
(2, does_not_raise()),
|
||||||
])
|
(1, does_not_raise()),
|
||||||
|
(0, pytest.raises(ZeroDivisionError)),
|
||||||
|
],
|
||||||
|
)
|
||||||
def test_division(example_input, expectation):
|
def test_division(example_input, expectation):
|
||||||
"""Test how much I know division."""
|
"""Test how much I know division."""
|
||||||
with expectation:
|
with expectation:
|
||||||
|
|
|
@ -139,6 +139,7 @@ that match ``*_check``. For example, if we have::
|
||||||
class CheckMyApp:
|
class CheckMyApp:
|
||||||
def simple_check(self):
|
def simple_check(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def complex_check(self):
|
def complex_check(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -267,7 +268,7 @@ and a ``setup.py`` dummy file like this::
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
# content of setup.py
|
# content of setup.py
|
||||||
0/0 # will raise exception if imported
|
0 / 0 # will raise exception if imported
|
||||||
|
|
||||||
If you run with a Python 2 interpreter then you will find the one test and will
|
If you run with a Python 2 interpreter then you will find the one test and will
|
||||||
leave out the ``setup.py`` file:
|
leave out the ``setup.py`` file:
|
||||||
|
|
|
@ -13,6 +13,7 @@ calls it::
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session", autouse=True)
|
@pytest.fixture(scope="session", autouse=True)
|
||||||
def callattr_ahead_of_alltests(request):
|
def callattr_ahead_of_alltests(request):
|
||||||
print("callattr_ahead_of_alltests called")
|
print("callattr_ahead_of_alltests called")
|
||||||
|
@ -22,7 +23,7 @@ calls it::
|
||||||
cls = item.getparent(pytest.Class)
|
cls = item.getparent(pytest.Class)
|
||||||
if cls not in seen:
|
if cls not in seen:
|
||||||
if hasattr(cls.obj, "callme"):
|
if hasattr(cls.obj, "callme"):
|
||||||
cls.obj.callme()
|
cls.obj.callme()
|
||||||
seen.add(cls)
|
seen.add(cls)
|
||||||
|
|
||||||
test classes may now define a ``callme`` method which
|
test classes may now define a ``callme`` method which
|
||||||
|
@ -32,6 +33,7 @@ will be called ahead of running any tests::
|
||||||
|
|
||||||
# content of test_module.py
|
# content of test_module.py
|
||||||
|
|
||||||
|
|
||||||
class TestHello:
|
class TestHello:
|
||||||
@classmethod
|
@classmethod
|
||||||
def callme(cls):
|
def callme(cls):
|
||||||
|
@ -43,16 +45,20 @@ will be called ahead of running any tests::
|
||||||
def test_method2(self):
|
def test_method2(self):
|
||||||
print("test_method1 called")
|
print("test_method1 called")
|
||||||
|
|
||||||
|
|
||||||
class TestOther:
|
class TestOther:
|
||||||
@classmethod
|
@classmethod
|
||||||
def callme(cls):
|
def callme(cls):
|
||||||
print("callme other called")
|
print("callme other called")
|
||||||
|
|
||||||
def test_other(self):
|
def test_other(self):
|
||||||
print("test other")
|
print("test other")
|
||||||
|
|
||||||
|
|
||||||
# works with unittest as well ...
|
# works with unittest as well ...
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class SomeTest(unittest.TestCase):
|
class SomeTest(unittest.TestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def callme(self):
|
def callme(self):
|
||||||
|
|
|
@ -56,15 +56,18 @@ using it::
|
||||||
# content of ./test_smtpsimple.py
|
# content of ./test_smtpsimple.py
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def smtp_connection():
|
def smtp_connection():
|
||||||
import smtplib
|
import smtplib
|
||||||
|
|
||||||
return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
|
return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
|
||||||
|
|
||||||
|
|
||||||
def test_ehlo(smtp_connection):
|
def test_ehlo(smtp_connection):
|
||||||
response, msg = smtp_connection.ehlo()
|
response, msg = smtp_connection.ehlo()
|
||||||
assert response == 250
|
assert response == 250
|
||||||
assert 0 # for demo purposes
|
assert 0 # for demo purposes
|
||||||
|
|
||||||
Here, the ``test_ehlo`` needs the ``smtp_connection`` fixture value. pytest
|
Here, the ``test_ehlo`` needs the ``smtp_connection`` fixture value. pytest
|
||||||
will discover and call the :py:func:`@pytest.fixture <_pytest.python.fixture>`
|
will discover and call the :py:func:`@pytest.fixture <_pytest.python.fixture>`
|
||||||
|
@ -190,6 +193,7 @@ access the fixture function::
|
||||||
import pytest
|
import pytest
|
||||||
import smtplib
|
import smtplib
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def smtp_connection():
|
def smtp_connection():
|
||||||
return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
|
return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
|
||||||
|
@ -203,12 +207,14 @@ located)::
|
||||||
|
|
||||||
# content of test_module.py
|
# content of test_module.py
|
||||||
|
|
||||||
|
|
||||||
def test_ehlo(smtp_connection):
|
def test_ehlo(smtp_connection):
|
||||||
response, msg = smtp_connection.ehlo()
|
response, msg = smtp_connection.ehlo()
|
||||||
assert response == 250
|
assert response == 250
|
||||||
assert b"smtp.gmail.com" in msg
|
assert b"smtp.gmail.com" in msg
|
||||||
assert 0 # for demo purposes
|
assert 0 # for demo purposes
|
||||||
|
|
||||||
|
|
||||||
def test_noop(smtp_connection):
|
def test_noop(smtp_connection):
|
||||||
response, msg = smtp_connection.noop()
|
response, msg = smtp_connection.noop()
|
||||||
assert response == 250
|
assert response == 250
|
||||||
|
@ -491,6 +497,7 @@ read an optional server URL from the test module which uses our fixture::
|
||||||
import pytest
|
import pytest
|
||||||
import smtplib
|
import smtplib
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def smtp_connection(request):
|
def smtp_connection(request):
|
||||||
server = getattr(request.module, "smtpserver", "smtp.gmail.com")
|
server = getattr(request.module, "smtpserver", "smtp.gmail.com")
|
||||||
|
@ -519,6 +526,7 @@ server URL in its module namespace::
|
||||||
|
|
||||||
smtpserver = "mail.python.org" # will be read by smtp fixture
|
smtpserver = "mail.python.org" # will be read by smtp fixture
|
||||||
|
|
||||||
|
|
||||||
def test_showhelo(smtp_connection):
|
def test_showhelo(smtp_connection):
|
||||||
assert 0, smtp_connection.helo()
|
assert 0, smtp_connection.helo()
|
||||||
|
|
||||||
|
@ -556,12 +564,8 @@ Factories can have parameters as needed::
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def make_customer_record():
|
def make_customer_record():
|
||||||
|
|
||||||
def _make_customer_record(name):
|
def _make_customer_record(name):
|
||||||
return {
|
return {"name": name, "orders": []}
|
||||||
"name": name,
|
|
||||||
"orders": []
|
|
||||||
}
|
|
||||||
|
|
||||||
return _make_customer_record
|
return _make_customer_record
|
||||||
|
|
||||||
|
@ -620,8 +624,8 @@ through the special :py:class:`request <FixtureRequest>` object::
|
||||||
import pytest
|
import pytest
|
||||||
import smtplib
|
import smtplib
|
||||||
|
|
||||||
@pytest.fixture(scope="module",
|
|
||||||
params=["smtp.gmail.com", "mail.python.org"])
|
@pytest.fixture(scope="module", params=["smtp.gmail.com", "mail.python.org"])
|
||||||
def smtp_connection(request):
|
def smtp_connection(request):
|
||||||
smtp_connection = smtplib.SMTP(request.param, 587, timeout=5)
|
smtp_connection = smtplib.SMTP(request.param, 587, timeout=5)
|
||||||
yield smtp_connection
|
yield smtp_connection
|
||||||
|
@ -713,23 +717,28 @@ the string used in a test ID for a certain fixture value by using the
|
||||||
# content of test_ids.py
|
# content of test_ids.py
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=[0, 1], ids=["spam", "ham"])
|
@pytest.fixture(params=[0, 1], ids=["spam", "ham"])
|
||||||
def a(request):
|
def a(request):
|
||||||
return request.param
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
def test_a(a):
|
def test_a(a):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def idfn(fixture_value):
|
def idfn(fixture_value):
|
||||||
if fixture_value == 0:
|
if fixture_value == 0:
|
||||||
return "eggs"
|
return "eggs"
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=[0, 1], ids=idfn)
|
@pytest.fixture(params=[0, 1], ids=idfn)
|
||||||
def b(request):
|
def b(request):
|
||||||
return request.param
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
def test_b(b):
|
def test_b(b):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -778,10 +787,13 @@ Example::
|
||||||
|
|
||||||
# content of test_fixture_marks.py
|
# content of test_fixture_marks.py
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=[0, 1, pytest.param(2, marks=pytest.mark.skip)])
|
@pytest.fixture(params=[0, 1, pytest.param(2, marks=pytest.mark.skip)])
|
||||||
def data_set(request):
|
def data_set(request):
|
||||||
return request.param
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
def test_data(data_set):
|
def test_data(data_set):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -820,14 +832,17 @@ and instantiate an object ``app`` where we stick the already defined
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
class App:
|
class App:
|
||||||
def __init__(self, smtp_connection):
|
def __init__(self, smtp_connection):
|
||||||
self.smtp_connection = smtp_connection
|
self.smtp_connection = smtp_connection
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def app(smtp_connection):
|
def app(smtp_connection):
|
||||||
return App(smtp_connection)
|
return App(smtp_connection)
|
||||||
|
|
||||||
|
|
||||||
def test_smtp_connection_exists(app):
|
def test_smtp_connection_exists(app):
|
||||||
assert app.smtp_connection
|
assert app.smtp_connection
|
||||||
|
|
||||||
|
@ -883,6 +898,7 @@ to show the setup/teardown flow::
|
||||||
# content of test_module.py
|
# content of test_module.py
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module", params=["mod1", "mod2"])
|
@pytest.fixture(scope="module", params=["mod1", "mod2"])
|
||||||
def modarg(request):
|
def modarg(request):
|
||||||
param = request.param
|
param = request.param
|
||||||
|
@ -890,17 +906,23 @@ to show the setup/teardown flow::
|
||||||
yield param
|
yield param
|
||||||
print(" TEARDOWN modarg %s" % param)
|
print(" TEARDOWN modarg %s" % param)
|
||||||
|
|
||||||
@pytest.fixture(scope="function", params=[1,2])
|
|
||||||
|
@pytest.fixture(scope="function", params=[1, 2])
|
||||||
def otherarg(request):
|
def otherarg(request):
|
||||||
param = request.param
|
param = request.param
|
||||||
print(" SETUP otherarg %s" % param)
|
print(" SETUP otherarg %s" % param)
|
||||||
yield param
|
yield param
|
||||||
print(" TEARDOWN otherarg %s" % param)
|
print(" TEARDOWN otherarg %s" % param)
|
||||||
|
|
||||||
|
|
||||||
def test_0(otherarg):
|
def test_0(otherarg):
|
||||||
print(" RUN test0 with otherarg %s" % otherarg)
|
print(" RUN test0 with otherarg %s" % otherarg)
|
||||||
|
|
||||||
|
|
||||||
def test_1(modarg):
|
def test_1(modarg):
|
||||||
print(" RUN test1 with modarg %s" % modarg)
|
print(" RUN test1 with modarg %s" % modarg)
|
||||||
|
|
||||||
|
|
||||||
def test_2(otherarg, modarg):
|
def test_2(otherarg, modarg):
|
||||||
print(" RUN test2 with otherarg {} and modarg {}".format(otherarg, modarg))
|
print(" RUN test2 with otherarg {} and modarg {}".format(otherarg, modarg))
|
||||||
|
|
||||||
|
@ -987,6 +1009,7 @@ file::
|
||||||
import tempfile
|
import tempfile
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def cleandir():
|
def cleandir():
|
||||||
newpath = tempfile.mkdtemp()
|
newpath = tempfile.mkdtemp()
|
||||||
|
@ -1000,6 +1023,7 @@ and declare its use in a test module via a ``usefixtures`` marker::
|
||||||
import os
|
import os
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("cleandir")
|
@pytest.mark.usefixtures("cleandir")
|
||||||
class TestDirectoryInit:
|
class TestDirectoryInit:
|
||||||
def test_cwd_starts_empty(self):
|
def test_cwd_starts_empty(self):
|
||||||
|
@ -1086,18 +1110,23 @@ self-contained implementation of this idea::
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
class DB:
|
class DB:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.intransaction = []
|
self.intransaction = []
|
||||||
|
|
||||||
def begin(self, name):
|
def begin(self, name):
|
||||||
self.intransaction.append(name)
|
self.intransaction.append(name)
|
||||||
|
|
||||||
def rollback(self):
|
def rollback(self):
|
||||||
self.intransaction.pop()
|
self.intransaction.pop()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def db():
|
def db():
|
||||||
return DB()
|
return DB()
|
||||||
|
|
||||||
|
|
||||||
class TestClass:
|
class TestClass:
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def transact(self, request, db):
|
def transact(self, request, db):
|
||||||
|
|
|
@ -29,13 +29,15 @@ a per-session Database object::
|
||||||
class Database:
|
class Database:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
print("database instance created")
|
print("database instance created")
|
||||||
|
|
||||||
def destroy(self):
|
def destroy(self):
|
||||||
print("database instance destroyed")
|
print("database instance destroyed")
|
||||||
|
|
||||||
|
|
||||||
def pytest_funcarg__db(request):
|
def pytest_funcarg__db(request):
|
||||||
return request.cached_setup(setup=DataBase,
|
return request.cached_setup(
|
||||||
teardown=lambda db: db.destroy,
|
setup=DataBase, teardown=lambda db: db.destroy, scope="session"
|
||||||
scope="session")
|
)
|
||||||
|
|
||||||
There are several limitations and difficulties with this approach:
|
There are several limitations and difficulties with this approach:
|
||||||
|
|
||||||
|
@ -100,7 +102,7 @@ sets. pytest-2.3 introduces a decorator for use on the factory itself::
|
||||||
|
|
||||||
@pytest.fixture(params=["mysql", "pg"])
|
@pytest.fixture(params=["mysql", "pg"])
|
||||||
def db(request):
|
def db(request):
|
||||||
... # use request.param
|
... # use request.param
|
||||||
|
|
||||||
Here the factory will be invoked twice (with the respective "mysql"
|
Here the factory will be invoked twice (with the respective "mysql"
|
||||||
and "pg" values set as ``request.param`` attributes) and all of
|
and "pg" values set as ``request.param`` attributes) and all of
|
||||||
|
|
|
@ -43,6 +43,7 @@ Create a simple test function with just four lines of code::
|
||||||
def func(x):
|
def func(x):
|
||||||
return x + 1
|
return x + 1
|
||||||
|
|
||||||
|
|
||||||
def test_answer():
|
def test_answer():
|
||||||
assert func(3) == 5
|
assert func(3) == 5
|
||||||
|
|
||||||
|
@ -91,9 +92,12 @@ Use the :ref:`raises <assertraises>` helper to assert that some code raises an e
|
||||||
|
|
||||||
# content of test_sysexit.py
|
# content of test_sysexit.py
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
|
||||||
def test_mytest():
|
def test_mytest():
|
||||||
with pytest.raises(SystemExit):
|
with pytest.raises(SystemExit):
|
||||||
f()
|
f()
|
||||||
|
@ -117,11 +121,11 @@ Once you develop multiple tests, you may want to group them into a class. pytest
|
||||||
class TestClass:
|
class TestClass:
|
||||||
def test_one(self):
|
def test_one(self):
|
||||||
x = "this"
|
x = "this"
|
||||||
assert 'h' in x
|
assert "h" in x
|
||||||
|
|
||||||
def test_two(self):
|
def test_two(self):
|
||||||
x = "hello"
|
x = "hello"
|
||||||
assert hasattr(x, 'check')
|
assert hasattr(x, "check")
|
||||||
|
|
||||||
``pytest`` discovers all tests following its :ref:`Conventions for Python test discovery <test discovery>`, so it finds both ``test_`` prefixed functions. There is no need to subclass anything. We can simply run the module by passing its filename:
|
``pytest`` discovers all tests following its :ref:`Conventions for Python test discovery <test discovery>`, so it finds both ``test_`` prefixed functions. There is no need to subclass anything. We can simply run the module by passing its filename:
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,7 @@ logger::
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def test_foo(caplog):
|
def test_foo(caplog):
|
||||||
caplog.set_level(logging.CRITICAL, logger='root.baz')
|
caplog.set_level(logging.CRITICAL, logger="root.baz")
|
||||||
pass
|
pass
|
||||||
|
|
||||||
The log levels set are restored automatically at the end of the test.
|
The log levels set are restored automatically at the end of the test.
|
||||||
|
@ -105,7 +105,7 @@ logger can be changed instead with::
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def test_bar(caplog):
|
def test_bar(caplog):
|
||||||
with caplog.at_level(logging.CRITICAL, logger='root.baz'):
|
with caplog.at_level(logging.CRITICAL, logger="root.baz"):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
Lastly all the logs sent to the logger during the test run are made available on
|
Lastly all the logs sent to the logger during the test run are made available on
|
||||||
|
@ -117,8 +117,8 @@ This is useful for when you want to assert on the contents of a message::
|
||||||
def test_baz(caplog):
|
def test_baz(caplog):
|
||||||
func_under_test()
|
func_under_test()
|
||||||
for record in caplog.records:
|
for record in caplog.records:
|
||||||
assert record.levelname != 'CRITICAL'
|
assert record.levelname != "CRITICAL"
|
||||||
assert 'wally' not in caplog.text
|
assert "wally" not in caplog.text
|
||||||
|
|
||||||
For all the available attributes of the log records see the
|
For all the available attributes of the log records see the
|
||||||
``logging.LogRecord`` class.
|
``logging.LogRecord`` class.
|
||||||
|
@ -130,11 +130,9 @@ severity and message::
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def test_foo(caplog):
|
def test_foo(caplog):
|
||||||
logging.getLogger().info('boo %s', 'arg')
|
logging.getLogger().info("boo %s", "arg")
|
||||||
|
|
||||||
assert caplog.record_tuples == [
|
assert caplog.record_tuples == [("root", logging.INFO, "boo arg")]
|
||||||
('root', logging.INFO, 'boo arg'),
|
|
||||||
]
|
|
||||||
|
|
||||||
You can call ``caplog.clear()`` to reset the captured log records in a test::
|
You can call ``caplog.clear()`` to reset the captured log records in a test::
|
||||||
|
|
||||||
|
@ -144,7 +142,7 @@ You can call ``caplog.clear()`` to reset the captured log records in a test::
|
||||||
some_method_that_creates_log_records()
|
some_method_that_creates_log_records()
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
your_test_method()
|
your_test_method()
|
||||||
assert ['Foo'] == [rec.message for rec in caplog.records]
|
assert ["Foo"] == [rec.message for rec in caplog.records]
|
||||||
|
|
||||||
|
|
||||||
The ``caplog.records`` attribute contains records from the current stage only, so
|
The ``caplog.records`` attribute contains records from the current stage only, so
|
||||||
|
|
|
@ -473,7 +473,7 @@ To use it, include in your top-most ``conftest.py`` file::
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
pytest_plugins = 'pytester'
|
pytest_plugins = "pytester"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -96,6 +96,8 @@ and more. Here is an example test usage::
|
||||||
|
|
||||||
# content of test_tmpdir.py
|
# content of test_tmpdir.py
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
def test_create_file(tmpdir):
|
def test_create_file(tmpdir):
|
||||||
p = tmpdir.mkdir("sub").join("hello.txt")
|
p = tmpdir.mkdir("sub").join("hello.txt")
|
||||||
p.write("content")
|
p.write("content")
|
||||||
|
|
|
@ -91,10 +91,12 @@ it from a unittest-style test::
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="class")
|
||||||
def db_class(request):
|
def db_class(request):
|
||||||
class DummyDB:
|
class DummyDB:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# set a class attribute on the invoking test context
|
# set a class attribute on the invoking test context
|
||||||
request.cls.db = DummyDB()
|
request.cls.db = DummyDB()
|
||||||
|
|
||||||
|
@ -116,14 +118,15 @@ fixture definition::
|
||||||
import unittest
|
import unittest
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("db_class")
|
@pytest.mark.usefixtures("db_class")
|
||||||
class MyTest(unittest.TestCase):
|
class MyTest(unittest.TestCase):
|
||||||
def test_method1(self):
|
def test_method1(self):
|
||||||
assert hasattr(self, "db")
|
assert hasattr(self, "db")
|
||||||
assert 0, self.db # fail for demo purposes
|
assert 0, self.db # fail for demo purposes
|
||||||
|
|
||||||
def test_method2(self):
|
def test_method2(self):
|
||||||
assert 0, self.db # fail for demo purposes
|
assert 0, self.db # fail for demo purposes
|
||||||
|
|
||||||
The ``@pytest.mark.usefixtures("db_class")`` class-decorator makes sure that
|
The ``@pytest.mark.usefixtures("db_class")`` class-decorator makes sure that
|
||||||
the pytest fixture function ``db_class`` is called once per class.
|
the pytest fixture function ``db_class`` is called once per class.
|
||||||
|
@ -193,11 +196,11 @@ creation of a per-test temporary directory::
|
||||||
import pytest
|
import pytest
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
class MyTest(unittest.TestCase):
|
|
||||||
|
|
||||||
|
class MyTest(unittest.TestCase):
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def initdir(self, tmpdir):
|
def initdir(self, tmpdir):
|
||||||
tmpdir.chdir() # change to pytest-provided temporary directory
|
tmpdir.chdir() # change to pytest-provided temporary directory
|
||||||
tmpdir.join("samplefile.ini").write("# testdata")
|
tmpdir.join("samplefile.ini").write("# testdata")
|
||||||
|
|
||||||
def test_method(self):
|
def test_method(self):
|
||||||
|
|
|
@ -766,7 +766,7 @@ You can pass in options and arguments::
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
pytest.main(['-x', 'mytestdir'])
|
pytest.main(["-x", "mytestdir"])
|
||||||
|
|
||||||
You can specify additional plugins to ``pytest.main``::
|
You can specify additional plugins to ``pytest.main``::
|
||||||
|
|
||||||
|
@ -774,10 +774,13 @@ You can specify additional plugins to ``pytest.main``::
|
||||||
|
|
||||||
# content of myinvoke.py
|
# content of myinvoke.py
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
class MyPlugin:
|
class MyPlugin:
|
||||||
def pytest_sessionfinish(self):
|
def pytest_sessionfinish(self):
|
||||||
print("*** test run reporting finishing")
|
print("*** test run reporting finishing")
|
||||||
|
|
||||||
|
|
||||||
pytest.main(["-qq"], plugins=[MyPlugin()])
|
pytest.main(["-qq"], plugins=[MyPlugin()])
|
||||||
|
|
||||||
Running it will show that ``MyPlugin`` was added and its
|
Running it will show that ``MyPlugin`` was added and its
|
||||||
|
|
|
@ -34,6 +34,7 @@ which will usually be called once for all the functions::
|
||||||
def setup_module(module):
|
def setup_module(module):
|
||||||
""" setup any state specific to the execution of the given module."""
|
""" setup any state specific to the execution of the given module."""
|
||||||
|
|
||||||
|
|
||||||
def teardown_module(module):
|
def teardown_module(module):
|
||||||
""" teardown any state that was previously setup with a setup_module
|
""" teardown any state that was previously setup with a setup_module
|
||||||
method.
|
method.
|
||||||
|
@ -55,6 +56,7 @@ and after all test methods of the class are called::
|
||||||
usually contains tests).
|
usually contains tests).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def teardown_class(cls):
|
def teardown_class(cls):
|
||||||
""" teardown any state that was previously setup with a call to
|
""" teardown any state that was previously setup with a call to
|
||||||
|
@ -73,6 +75,7 @@ Similarly, the following methods are called around each method invocation::
|
||||||
class. setup_method is invoked for every test method of a class.
|
class. setup_method is invoked for every test method of a class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def teardown_method(self, method):
|
def teardown_method(self, method):
|
||||||
""" teardown any state that was previously setup with a setup_method
|
""" teardown any state that was previously setup with a setup_method
|
||||||
call.
|
call.
|
||||||
|
@ -90,6 +93,7 @@ you can also use the following functions to implement fixtures::
|
||||||
Invoked for every test function in the module.
|
Invoked for every test function in the module.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def teardown_function(function):
|
def teardown_function(function):
|
||||||
""" teardown any state that was previously setup with a setup_function
|
""" teardown any state that was previously setup with a setup_function
|
||||||
call.
|
call.
|
||||||
|
|
Loading…
Reference in New Issue