blacken docs
This commit is contained in:
parent
5c878001ea
commit
0f2d7dc73c
|
@ -5,7 +5,13 @@ repos:
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
args: [--safe, --quiet]
|
args: [--safe, --quiet]
|
||||||
python_version: python3.6
|
language_version: python3.6
|
||||||
|
- repo: https://github.com/asottile/blacken-docs
|
||||||
|
rev: v0.1.1
|
||||||
|
hooks:
|
||||||
|
- id: blacken-docs
|
||||||
|
additional_dependencies: [black==18.5b1]
|
||||||
|
language_version: python3.6
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v1.2.3
|
rev: v1.2.3
|
||||||
hooks:
|
hooks:
|
||||||
|
|
|
@ -40,6 +40,7 @@ An example of a simple test:
|
||||||
def inc(x):
|
def inc(x):
|
||||||
return x + 1
|
return x + 1
|
||||||
|
|
||||||
|
|
||||||
def test_answer():
|
def test_answer():
|
||||||
assert inc(3) == 5
|
assert inc(3) == 5
|
||||||
|
|
||||||
|
|
|
@ -145,9 +145,9 @@ as a context manager, disabling capture inside the ``with`` block:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def test_disabling_capturing(capsys):
|
def test_disabling_capturing(capsys):
|
||||||
print('this output is captured')
|
print("this output is captured")
|
||||||
with capsys.disabled():
|
with capsys.disabled():
|
||||||
print('output not captured, going directly to sys.stdout')
|
print("output not captured, going directly to sys.stdout")
|
||||||
print('this output is also captured')
|
print("this output is also captured")
|
||||||
|
|
||||||
.. include:: links.inc
|
.. include:: links.inc
|
||||||
|
|
|
@ -9,15 +9,18 @@ example: specifying and selecting acceptance tests
|
||||||
# ./conftest.py
|
# ./conftest.py
|
||||||
def pytest_option(parser):
|
def pytest_option(parser):
|
||||||
group = parser.getgroup("myproject")
|
group = parser.getgroup("myproject")
|
||||||
group.addoption("-A", dest="acceptance", action="store_true",
|
group.addoption(
|
||||||
help="run (slow) acceptance tests")
|
"-A", dest="acceptance", action="store_true", help="run (slow) acceptance tests"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def pytest_funcarg__accept(request):
|
def pytest_funcarg__accept(request):
|
||||||
return AcceptFixture(request)
|
return AcceptFixture(request)
|
||||||
|
|
||||||
|
|
||||||
class AcceptFixture(object):
|
class AcceptFixture(object):
|
||||||
def __init__(self, request):
|
def __init__(self, request):
|
||||||
if not request.config.getoption('acceptance'):
|
if not request.config.getoption("acceptance"):
|
||||||
pytest.skip("specify -A to run acceptance tests")
|
pytest.skip("specify -A to run acceptance tests")
|
||||||
self.tmpdir = request.config.mktemp(request.function.__name__, numbered=True)
|
self.tmpdir = request.config.mktemp(request.function.__name__, numbered=True)
|
||||||
|
|
||||||
|
@ -61,6 +64,7 @@ extend the `accept example`_ by putting this in our test module:
|
||||||
arg.tmpdir.mkdir("special")
|
arg.tmpdir.mkdir("special")
|
||||||
return arg
|
return arg
|
||||||
|
|
||||||
|
|
||||||
class TestSpecialAcceptance(object):
|
class TestSpecialAcceptance(object):
|
||||||
def test_sometest(self, accept):
|
def test_sometest(self, accept):
|
||||||
assert accept.tmpdir.join("special").check()
|
assert accept.tmpdir.join("special").check()
|
||||||
|
|
|
@ -32,9 +32,12 @@ provide the ``cmdopt`` through a :ref:`fixture function <fixture function>`:
|
||||||
# content of conftest.py
|
# content of conftest.py
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
parser.addoption("--cmdopt", action="store", default="type1",
|
parser.addoption(
|
||||||
help="my option: type1 or type2")
|
"--cmdopt", action="store", default="type1", help="my option: type1 or type2"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def cmdopt(request):
|
def cmdopt(request):
|
||||||
|
@ -102,9 +105,12 @@ the command line arguments before they get processed:
|
||||||
|
|
||||||
# content of conftest.py
|
# content of conftest.py
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
def pytest_load_initial_conftests(args):
|
def pytest_load_initial_conftests(args):
|
||||||
if 'xdist' in sys.modules: # pytest-xdist plugin
|
if "xdist" in sys.modules: # pytest-xdist plugin
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
|
|
||||||
num = max(multiprocessing.cpu_count() / 2, 1)
|
num = max(multiprocessing.cpu_count() / 2, 1)
|
||||||
args[:] = ["-n", str(num)] + args
|
args[:] = ["-n", str(num)] + args
|
||||||
|
|
||||||
|
@ -136,9 +142,13 @@ line option to control skipping of ``pytest.mark.slow`` marked tests:
|
||||||
# content of conftest.py
|
# content of conftest.py
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
parser.addoption("--runslow", action="store_true",
|
parser.addoption(
|
||||||
default=False, help="run slow tests")
|
"--runslow", action="store_true", default=False, help="run slow tests"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def pytest_collection_modifyitems(config, items):
|
def pytest_collection_modifyitems(config, items):
|
||||||
if config.getoption("--runslow"):
|
if config.getoption("--runslow"):
|
||||||
|
@ -206,11 +216,14 @@ Example:
|
||||||
|
|
||||||
# content of test_checkconfig.py
|
# content of test_checkconfig.py
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
def checkconfig(x):
|
def checkconfig(x):
|
||||||
__tracebackhide__ = True
|
__tracebackhide__ = True
|
||||||
if not hasattr(x, "config"):
|
if not hasattr(x, "config"):
|
||||||
pytest.fail("not configured: %s" % (x,))
|
pytest.fail("not configured: %s" % (x,))
|
||||||
|
|
||||||
|
|
||||||
def test_something():
|
def test_something():
|
||||||
checkconfig(42)
|
checkconfig(42)
|
||||||
|
|
||||||
|
@ -240,14 +253,17 @@ this to make sure unexpected exception types aren't hidden:
|
||||||
import operator
|
import operator
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
class ConfigException(Exception):
|
class ConfigException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def checkconfig(x):
|
def checkconfig(x):
|
||||||
__tracebackhide__ = operator.methodcaller('errisinstance', ConfigException)
|
__tracebackhide__ = operator.methodcaller("errisinstance", ConfigException)
|
||||||
if not hasattr(x, "config"):
|
if not hasattr(x, "config"):
|
||||||
raise ConfigException("not configured: %s" % (x,))
|
raise ConfigException("not configured: %s" % (x,))
|
||||||
|
|
||||||
|
|
||||||
def test_something():
|
def test_something():
|
||||||
checkconfig(42)
|
checkconfig(42)
|
||||||
|
|
||||||
|
@ -269,19 +285,23 @@ running from a test you can do something like this:
|
||||||
|
|
||||||
# content of conftest.py
|
# content of conftest.py
|
||||||
|
|
||||||
|
|
||||||
def pytest_configure(config):
|
def pytest_configure(config):
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
sys._called_from_test = True
|
sys._called_from_test = True
|
||||||
|
|
||||||
|
|
||||||
def pytest_unconfigure(config):
|
def pytest_unconfigure(config):
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
del sys._called_from_test
|
del sys._called_from_test
|
||||||
|
|
||||||
and then check for the ``sys._called_from_test`` flag:
|
and then check for the ``sys._called_from_test`` flag:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
if hasattr(sys, '_called_from_test'):
|
if hasattr(sys, "_called_from_test"):
|
||||||
# called from within a test run
|
# called from within a test run
|
||||||
...
|
...
|
||||||
else:
|
else:
|
||||||
|
@ -303,6 +323,7 @@ It's easy to present extra information in a ``pytest`` run:
|
||||||
|
|
||||||
# content of conftest.py
|
# content of conftest.py
|
||||||
|
|
||||||
|
|
||||||
def pytest_report_header(config):
|
def pytest_report_header(config):
|
||||||
return "project deps: mylib-1.1"
|
return "project deps: mylib-1.1"
|
||||||
|
|
||||||
|
@ -327,8 +348,9 @@ display more information if applicable:
|
||||||
|
|
||||||
# content of conftest.py
|
# content of conftest.py
|
||||||
|
|
||||||
|
|
||||||
def pytest_report_header(config):
|
def pytest_report_header(config):
|
||||||
if config.getoption('verbose') > 0:
|
if config.getoption("verbose") > 0:
|
||||||
return ["info1: did you know that ...", "did you?"]
|
return ["info1: did you know that ...", "did you?"]
|
||||||
|
|
||||||
which will add info only when run with "--v"::
|
which will add info only when run with "--v"::
|
||||||
|
@ -369,12 +391,15 @@ out which tests are the slowest. Let's make an artificial test suite:
|
||||||
# content of test_some_are_slow.py
|
# content of test_some_are_slow.py
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
|
||||||
def test_funcfast():
|
def test_funcfast():
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
|
||||||
def test_funcslow1():
|
def test_funcslow1():
|
||||||
time.sleep(0.2)
|
time.sleep(0.2)
|
||||||
|
|
||||||
|
|
||||||
def test_funcslow2():
|
def test_funcslow2():
|
||||||
time.sleep(0.3)
|
time.sleep(0.3)
|
||||||
|
|
||||||
|
@ -411,12 +436,14 @@ an ``incremental`` marker which is to be used on classes:
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
def pytest_runtest_makereport(item, call):
|
def pytest_runtest_makereport(item, call):
|
||||||
if "incremental" in item.keywords:
|
if "incremental" in item.keywords:
|
||||||
if call.excinfo is not None:
|
if call.excinfo is not None:
|
||||||
parent = item.parent
|
parent = item.parent
|
||||||
parent._previousfailed = item
|
parent._previousfailed = item
|
||||||
|
|
||||||
|
|
||||||
def pytest_runtest_setup(item):
|
def pytest_runtest_setup(item):
|
||||||
if "incremental" in item.keywords:
|
if "incremental" in item.keywords:
|
||||||
previousfailed = getattr(item.parent, "_previousfailed", None)
|
previousfailed = getattr(item.parent, "_previousfailed", None)
|
||||||
|
@ -432,15 +459,19 @@ tests in a class. Here is a test module example:
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.incremental
|
@pytest.mark.incremental
|
||||||
class TestUserHandling(object):
|
class TestUserHandling(object):
|
||||||
def test_login(self):
|
def test_login(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_modification(self):
|
def test_modification(self):
|
||||||
assert 0
|
assert 0
|
||||||
|
|
||||||
def test_deletion(self):
|
def test_deletion(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def test_normal():
|
def test_normal():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -491,9 +522,11 @@ Here is an example for making a ``db`` fixture available in a directory:
|
||||||
# content of a/conftest.py
|
# content of a/conftest.py
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
class DB(object):
|
class DB(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def db():
|
def db():
|
||||||
return DB()
|
return DB()
|
||||||
|
@ -602,6 +635,7 @@ case we just write some information out to a ``failures`` file:
|
||||||
import pytest
|
import pytest
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
|
|
||||||
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
|
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
|
||||||
def pytest_runtest_makereport(item, call):
|
def pytest_runtest_makereport(item, call):
|
||||||
# execute all other hooks to obtain the report object
|
# execute all other hooks to obtain the report object
|
||||||
|
@ -628,6 +662,8 @@ if you then have failing tests:
|
||||||
# content of test_module.py
|
# content of test_module.py
|
||||||
def test_fail1(tmpdir):
|
def test_fail1(tmpdir):
|
||||||
assert 0
|
assert 0
|
||||||
|
|
||||||
|
|
||||||
def test_fail2():
|
def test_fail2():
|
||||||
assert 0
|
assert 0
|
||||||
|
|
||||||
|
@ -680,6 +716,7 @@ here is a little example implemented via a local plugin:
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
|
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
|
||||||
def pytest_runtest_makereport(item, call):
|
def pytest_runtest_makereport(item, call):
|
||||||
# execute all other hooks to obtain the report object
|
# execute all other hooks to obtain the report object
|
||||||
|
@ -712,16 +749,20 @@ if you then have failing tests:
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def other():
|
def other():
|
||||||
assert 0
|
assert 0
|
||||||
|
|
||||||
|
|
||||||
def test_setup_fails(something, other):
|
def test_setup_fails(something, other):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def test_call_fails(something):
|
def test_call_fails(something):
|
||||||
assert 0
|
assert 0
|
||||||
|
|
||||||
|
|
||||||
def test_fail2():
|
def test_fail2():
|
||||||
assert 0
|
assert 0
|
||||||
|
|
||||||
|
@ -789,7 +830,7 @@ test got stuck if necessary:
|
||||||
|
|
||||||
for pid in psutil.pids():
|
for pid in psutil.pids():
|
||||||
environ = psutil.Process(pid).environ()
|
environ = psutil.Process(pid).environ()
|
||||||
if 'PYTEST_CURRENT_TEST' in environ:
|
if "PYTEST_CURRENT_TEST" in environ:
|
||||||
print(f'pytest process {pid} running: {environ["PYTEST_CURRENT_TEST"]}')
|
print(f'pytest process {pid} running: {environ["PYTEST_CURRENT_TEST"]}')
|
||||||
|
|
||||||
During the test session pytest will set ``PYTEST_CURRENT_TEST`` to the current test
|
During the test session pytest will set ``PYTEST_CURRENT_TEST`` to the current test
|
||||||
|
@ -843,8 +884,9 @@ like ``pytest-timeout`` they must be imported explicitly and passed on to pytest
|
||||||
import sys
|
import sys
|
||||||
import pytest_timeout # Third party plugin
|
import pytest_timeout # Third party plugin
|
||||||
|
|
||||||
if len(sys.argv) > 1 and sys.argv[1] == '--pytest':
|
if len(sys.argv) > 1 and sys.argv[1] == "--pytest":
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
sys.exit(pytest.main(sys.argv[2:], plugins=[pytest_timeout]))
|
sys.exit(pytest.main(sys.argv[2:], plugins=[pytest_timeout]))
|
||||||
else:
|
else:
|
||||||
# normal application execution: at this point argv can be parsed
|
# normal application execution: at this point argv can be parsed
|
||||||
|
|
|
@ -275,18 +275,22 @@ Consider the code below:
|
||||||
def s1():
|
def s1():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def m1():
|
def m1():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def f1(tmpdir):
|
def f1(tmpdir):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def f2():
|
def f2():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def test_foo(f1, m1, f2, s1):
|
def test_foo(f1, m1, f2, s1):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@ -317,6 +321,7 @@ the code after the *yield* statement serves as the teardown code:
|
||||||
import smtplib
|
import smtplib
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def smtp():
|
def smtp():
|
||||||
smtp = smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
|
smtp = smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
|
||||||
|
@ -351,6 +356,7 @@ Note that we can also seamlessly use the ``yield`` syntax with ``with`` statemen
|
||||||
import smtplib
|
import smtplib
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def smtp():
|
def smtp():
|
||||||
with smtplib.SMTP("smtp.gmail.com", 587, timeout=5) as smtp:
|
with smtplib.SMTP("smtp.gmail.com", 587, timeout=5) as smtp:
|
||||||
|
@ -376,12 +382,15 @@ Here's the ``smtp`` fixture changed to use ``addfinalizer`` for cleanup:
|
||||||
import smtplib
|
import smtplib
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def smtp(request):
|
def smtp(request):
|
||||||
smtp = smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
|
smtp = smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
|
||||||
|
|
||||||
def fin():
|
def fin():
|
||||||
print("teardown smtp")
|
print("teardown smtp")
|
||||||
smtp.close()
|
smtp.close()
|
||||||
|
|
||||||
request.addfinalizer(fin)
|
request.addfinalizer(fin)
|
||||||
return smtp # provide the fixture value
|
return smtp # provide the fixture value
|
||||||
|
|
||||||
|
@ -868,7 +877,8 @@ You can specify multiple fixtures like this:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@pytest.mark.usefixtures("cleandir", "anotherfixture")
|
@pytest.mark.usefixtures("cleandir", "anotherfixture")
|
||||||
def test(): ...
|
def test():
|
||||||
|
...
|
||||||
|
|
||||||
and you may specify fixture usage at the test module level, using
|
and you may specify fixture usage at the test module level, using
|
||||||
a generic feature of the mark mechanism:
|
a generic feature of the mark mechanism:
|
||||||
|
|
|
@ -215,8 +215,8 @@ Add this to ``setup.py`` file:
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
# ...,
|
# ...,
|
||||||
setup_requires=['pytest-runner', ...],
|
setup_requires=["pytest-runner", ...],
|
||||||
tests_require=['pytest', ...],
|
tests_require=["pytest", ...],
|
||||||
# ...,
|
# ...,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -263,24 +263,26 @@ your own setuptools Test command for invoking pytest.
|
||||||
|
|
||||||
|
|
||||||
class PyTest(TestCommand):
|
class PyTest(TestCommand):
|
||||||
user_options = [('pytest-args=', 'a', "Arguments to pass to pytest")]
|
user_options = [("pytest-args=", "a", "Arguments to pass to pytest")]
|
||||||
|
|
||||||
def initialize_options(self):
|
def initialize_options(self):
|
||||||
TestCommand.initialize_options(self)
|
TestCommand.initialize_options(self)
|
||||||
self.pytest_args = ''
|
self.pytest_args = ""
|
||||||
|
|
||||||
def run_tests(self):
|
def run_tests(self):
|
||||||
import shlex
|
import shlex
|
||||||
|
|
||||||
# import here, cause outside the eggs aren't loaded
|
# import here, cause outside the eggs aren't loaded
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
errno = pytest.main(shlex.split(self.pytest_args))
|
errno = pytest.main(shlex.split(self.pytest_args))
|
||||||
sys.exit(errno)
|
sys.exit(errno)
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
# ...,
|
# ...,
|
||||||
tests_require=['pytest'],
|
tests_require=["pytest"],
|
||||||
cmdclass = {'test': PyTest},
|
cmdclass={"test": PyTest},
|
||||||
)
|
)
|
||||||
|
|
||||||
Now if you run::
|
Now if you run::
|
||||||
|
|
|
@ -17,6 +17,7 @@ An example of a simple test:
|
||||||
def inc(x):
|
def inc(x):
|
||||||
return x + 1
|
return x + 1
|
||||||
|
|
||||||
|
|
||||||
def test_answer():
|
def test_answer():
|
||||||
assert inc(3) == 5
|
assert inc(3) == 5
|
||||||
|
|
||||||
|
|
|
@ -138,10 +138,14 @@ the records for the ``setup`` and ``call`` stages during teardown like so:
|
||||||
def window(caplog):
|
def window(caplog):
|
||||||
window = create_window()
|
window = create_window()
|
||||||
yield window
|
yield window
|
||||||
for when in ('setup', 'call'):
|
for when in ("setup", "call"):
|
||||||
messages = [x.message for x in caplog.get_records(when) if x.level == logging.WARNING]
|
messages = [
|
||||||
|
x.message for x in caplog.get_records(when) if x.level == logging.WARNING
|
||||||
|
]
|
||||||
if messages:
|
if messages:
|
||||||
pytest.fail('warning messages encountered during testing: {}'.format(messages))
|
pytest.fail(
|
||||||
|
"warning messages encountered during testing: {}".format(messages)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -70,12 +70,12 @@ In general there are two scenarios on how markers should be handled:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
# replace this:
|
# replace this:
|
||||||
marker = item.get_marker('log_level')
|
marker = item.get_marker("log_level")
|
||||||
if marker:
|
if marker:
|
||||||
level = marker.args[0]
|
level = marker.args[0]
|
||||||
|
|
||||||
# by this:
|
# by this:
|
||||||
marker = item.get_closest_marker('log_level')
|
marker = item.get_closest_marker("log_level")
|
||||||
if marker:
|
if marker:
|
||||||
level = marker.args[0]
|
level = marker.args[0]
|
||||||
|
|
||||||
|
@ -87,14 +87,14 @@ order doesn't even matter. You probably want to think of your marks as a set her
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
# replace this
|
# replace this
|
||||||
skipif = item.get_marker('skipif')
|
skipif = item.get_marker("skipif")
|
||||||
if skipif:
|
if skipif:
|
||||||
for condition in skipif.args:
|
for condition in skipif.args:
|
||||||
# eval condition
|
# eval condition
|
||||||
...
|
...
|
||||||
|
|
||||||
# by this:
|
# by this:
|
||||||
for skipif in item.iter_markers('skipif'):
|
for skipif in item.iter_markers("skipif"):
|
||||||
condition = skipif.args[0]
|
condition = skipif.args[0]
|
||||||
# eval condition
|
# eval condition
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,8 @@ so that any attempts within tests to create http requests will fail.
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
import functools
|
import functools
|
||||||
|
|
||||||
|
|
||||||
def test_partial(monkeypatch):
|
def test_partial(monkeypatch):
|
||||||
with monkeypatch.context() as m:
|
with monkeypatch.context() as m:
|
||||||
m.setattr(functools, "partial", 3)
|
m.setattr(functools, "partial", 3)
|
||||||
|
|
|
@ -35,26 +35,29 @@ This is how a functional test could look like:
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def default_context():
|
def default_context():
|
||||||
return {'extra_context': {}}
|
return {"extra_context": {}}
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=[
|
@pytest.fixture(
|
||||||
{'author': 'alice'},
|
params=[
|
||||||
{'project_slug': 'helloworld'},
|
{"author": "alice"},
|
||||||
{'author': 'bob', 'project_slug': 'foobar'},
|
{"project_slug": "helloworld"},
|
||||||
])
|
{"author": "bob", "project_slug": "foobar"},
|
||||||
|
]
|
||||||
|
)
|
||||||
def extra_context(request):
|
def extra_context(request):
|
||||||
return {'extra_context': request.param}
|
return {"extra_context": request.param}
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=['default', 'extra'])
|
@pytest.fixture(params=["default", "extra"])
|
||||||
def context(request):
|
def context(request):
|
||||||
if request.param == 'default':
|
if request.param == "default":
|
||||||
return request.getfuncargvalue('default_context')
|
return request.getfuncargvalue("default_context")
|
||||||
else:
|
else:
|
||||||
return request.getfuncargvalue('extra_context')
|
return request.getfuncargvalue("extra_context")
|
||||||
|
|
||||||
|
|
||||||
def test_generate_project(cookies, context):
|
def test_generate_project(cookies, context):
|
||||||
|
@ -95,8 +98,7 @@ fixtures from existing ones.
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
pytest.define_combined_fixture(
|
pytest.define_combined_fixture(
|
||||||
name='context',
|
name="context", fixtures=["default_context", "extra_context"]
|
||||||
fixtures=['default_context', 'extra_context'],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
The new fixture ``context`` inherits the scope from the used fixtures and yield
|
The new fixture ``context`` inherits the scope from the used fixtures and yield
|
||||||
|
@ -123,10 +125,12 @@ all parameters marked as a fixture.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@pytest.fixture(params=[
|
@pytest.fixture(
|
||||||
pytest.fixture_request('default_context'),
|
params=[
|
||||||
pytest.fixture_request('extra_context'),
|
pytest.fixture_request("default_context"),
|
||||||
])
|
pytest.fixture_request("extra_context"),
|
||||||
|
]
|
||||||
|
)
|
||||||
def context(request):
|
def context(request):
|
||||||
"""Returns all values for ``default_context``, one-by-one before it
|
"""Returns all values for ``default_context``, one-by-one before it
|
||||||
does the same for ``extra_context``.
|
does the same for ``extra_context``.
|
||||||
|
@ -145,10 +149,10 @@ The same helper can be used in combination with ``pytest.mark.parametrize``.
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'context, expected_response_code',
|
"context, expected_response_code",
|
||||||
[
|
[
|
||||||
(pytest.fixture_request('default_context'), 0),
|
(pytest.fixture_request("default_context"), 0),
|
||||||
(pytest.fixture_request('extra_context'), 0),
|
(pytest.fixture_request("extra_context"), 0),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_generate_project(cookies, context, exit_code):
|
def test_generate_project(cookies, context, exit_code):
|
||||||
|
|
|
@ -198,7 +198,7 @@ For example:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@pytest.mark.timeout(10, 'slow', method='thread')
|
@pytest.mark.timeout(10, "slow", method="thread")
|
||||||
def test_function():
|
def test_function():
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@ -208,8 +208,8 @@ Will create and attach a :class:`Mark <_pytest.mark.structures.Mark>` object to
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
mark.args == (10, 'slow')
|
mark.args == (10, "slow")
|
||||||
mark.kwargs == {'method': 'thread'}
|
mark.kwargs == {"method": "thread"}
|
||||||
|
|
||||||
|
|
||||||
Fixtures
|
Fixtures
|
||||||
|
@ -225,9 +225,9 @@ Example of a test requiring a fixture:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def test_output(capsys):
|
def test_output(capsys):
|
||||||
print('hello')
|
print("hello")
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert out == 'hello\n'
|
assert out == "hello\n"
|
||||||
|
|
||||||
|
|
||||||
Example of a fixture requiring another fixture:
|
Example of a fixture requiring another fixture:
|
||||||
|
@ -236,7 +236,7 @@ Example of a fixture requiring another fixture:
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def db_session(tmpdir):
|
def db_session(tmpdir):
|
||||||
fn = tmpdir / 'db.file'
|
fn = tmpdir / "db.file"
|
||||||
return connect(str(fn))
|
return connect(str(fn))
|
||||||
|
|
||||||
For more details, consult the full :ref:`fixtures docs <fixture>`.
|
For more details, consult the full :ref:`fixtures docs <fixture>`.
|
||||||
|
@ -368,7 +368,7 @@ doctest_namespace
|
||||||
|
|
||||||
@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
|
||||||
|
|
||||||
For more details: :ref:`doctest_namespace`.
|
For more details: :ref:`doctest_namespace`.
|
||||||
|
|
||||||
|
@ -805,12 +805,14 @@ test functions and methods. Can be either a single mark or a sequence of marks.
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
pytestmark = pytest.mark.webtest
|
pytestmark = pytest.mark.webtest
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
pytestmark = (pytest.mark.integration, pytest.mark.slow)
|
pytestmark = (pytest.mark.integration, pytest.mark.slow)
|
||||||
|
|
||||||
PYTEST_DONT_REWRITE (module docstring)
|
PYTEST_DONT_REWRITE (module docstring)
|
||||||
|
|
|
@ -192,19 +192,19 @@ Here's a quick guide on how to skip tests in a module in different situations:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
pytestmark = pytest.mark.skip('all tests still WIP')
|
pytestmark = pytest.mark.skip("all tests still WIP")
|
||||||
|
|
||||||
2. Skip all tests in a module based on some condition:
|
2. Skip all tests in a module based on some condition:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
pytestmark = pytest.mark.skipif(sys.platform == 'win32', 'tests for linux only')
|
pytestmark = pytest.mark.skipif(sys.platform == "win32", "tests for linux only")
|
||||||
|
|
||||||
3. Skip all tests in a module if some import is missing:
|
3. Skip all tests in a module if some import is missing:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
pexpect = pytest.importorskip('pexpect')
|
pexpect = pytest.importorskip("pexpect")
|
||||||
|
|
||||||
|
|
||||||
.. _xfail:
|
.. _xfail:
|
||||||
|
@ -364,14 +364,20 @@ test instances when using parametrize:
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.mark.parametrize(("n", "expected"), [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("n", "expected"),
|
||||||
|
[
|
||||||
(1, 2),
|
(1, 2),
|
||||||
pytest.param(1, 0, marks=pytest.mark.xfail),
|
pytest.param(1, 0, marks=pytest.mark.xfail),
|
||||||
pytest.param(1, 3, marks=pytest.mark.xfail(reason="some bug")),
|
pytest.param(1, 3, marks=pytest.mark.xfail(reason="some bug")),
|
||||||
(2, 3),
|
(2, 3),
|
||||||
(3, 4),
|
(3, 4),
|
||||||
(4, 5),
|
(4, 5),
|
||||||
pytest.param(10, 11, marks=pytest.mark.skipif(sys.version_info >= (3, 0), reason="py2k")),
|
pytest.param(
|
||||||
])
|
10, 11, marks=pytest.mark.skipif(sys.version_info >= (3, 0), reason="py2k")
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
def test_increment(n, expected):
|
def test_increment(n, expected):
|
||||||
assert n + 1 == expected
|
assert n + 1 == expected
|
||||||
|
|
|
@ -71,13 +71,15 @@ to save time:
|
||||||
# contents of conftest.py
|
# contents of conftest.py
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.fixture(scope='session')
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
def image_file(tmpdir_factory):
|
def image_file(tmpdir_factory):
|
||||||
img = compute_expensive_image()
|
img = compute_expensive_image()
|
||||||
fn = tmpdir_factory.mktemp('data').join('img.png')
|
fn = tmpdir_factory.mktemp("data").join("img.png")
|
||||||
img.save(str(fn))
|
img.save(str(fn))
|
||||||
return fn
|
return fn
|
||||||
|
|
||||||
|
|
||||||
# contents of test_image.py
|
# contents of test_image.py
|
||||||
def test_histogram(image_file):
|
def test_histogram(image_file):
|
||||||
img = load_image(image_file)
|
img = load_image(image_file)
|
||||||
|
|
|
@ -272,11 +272,12 @@ Alternatively, you can integrate this functionality with custom markers:
|
||||||
|
|
||||||
# content of conftest.py
|
# content of conftest.py
|
||||||
|
|
||||||
|
|
||||||
def pytest_collection_modifyitems(session, config, items):
|
def pytest_collection_modifyitems(session, config, items):
|
||||||
for item in items:
|
for item in items:
|
||||||
for marker in item.iter_markers(name='test_id'):
|
for marker in item.iter_markers(name="test_id"):
|
||||||
test_id = marker.args[0]
|
test_id = marker.args[0]
|
||||||
item.user_properties.append(('test_id', test_id))
|
item.user_properties.append(("test_id", test_id))
|
||||||
|
|
||||||
And in your tests:
|
And in your tests:
|
||||||
|
|
||||||
|
@ -284,6 +285,8 @@ And in your tests:
|
||||||
|
|
||||||
# content of test_function.py
|
# content of test_function.py
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.test_id(1501)
|
@pytest.mark.test_id(1501)
|
||||||
def test_function():
|
def test_function():
|
||||||
assert True
|
assert True
|
||||||
|
@ -318,7 +321,7 @@ To add an additional xml attribute to a testcase element, you can use
|
||||||
def test_function(record_xml_attribute):
|
def test_function(record_xml_attribute):
|
||||||
record_xml_attribute("assertions", "REQ-1234")
|
record_xml_attribute("assertions", "REQ-1234")
|
||||||
record_xml_attribute("classname", "custom_classname")
|
record_xml_attribute("classname", "custom_classname")
|
||||||
print('hello world')
|
print("hello world")
|
||||||
assert True
|
assert True
|
||||||
|
|
||||||
Unlike ``record_property``, this will not add a new child element.
|
Unlike ``record_property``, this will not add a new child element.
|
||||||
|
@ -377,19 +380,22 @@ to all testcases you can use ``LogXML.add_global_properties``
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def log_global_env_facts(f):
|
def log_global_env_facts(f):
|
||||||
|
|
||||||
if pytest.config.pluginmanager.hasplugin('junitxml'):
|
if pytest.config.pluginmanager.hasplugin("junitxml"):
|
||||||
my_junit = getattr(pytest.config, '_xml', None)
|
my_junit = getattr(pytest.config, "_xml", None)
|
||||||
|
|
||||||
|
my_junit.add_global_property("ARCH", "PPC")
|
||||||
|
my_junit.add_global_property("STORAGE_TYPE", "CEPH")
|
||||||
|
|
||||||
my_junit.add_global_property('ARCH', 'PPC')
|
|
||||||
my_junit.add_global_property('STORAGE_TYPE', 'CEPH')
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures(log_global_env_facts.__name__)
|
@pytest.mark.usefixtures(log_global_env_facts.__name__)
|
||||||
def start_and_prepare_env():
|
def start_and_prepare_env():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TestMe(object):
|
class TestMe(object):
|
||||||
def test_foo(self):
|
def test_foo(self):
|
||||||
assert True
|
assert True
|
||||||
|
|
|
@ -94,11 +94,13 @@ even module level:
|
||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
|
|
||||||
def api_v1():
|
def api_v1():
|
||||||
warnings.warn(UserWarning("api v1, should use functions from v2"))
|
warnings.warn(UserWarning("api v1, should use functions from v2"))
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
@pytest.mark.filterwarnings('ignore:api v1')
|
|
||||||
|
@pytest.mark.filterwarnings("ignore:api v1")
|
||||||
def test_one():
|
def test_one():
|
||||||
assert api_v1() == 1
|
assert api_v1() == 1
|
||||||
|
|
||||||
|
@ -112,7 +114,7 @@ decorator or to all tests in a module by setting the ``pytestmark`` variable:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
# turns all warnings into errors for this module
|
# turns all warnings into errors for this module
|
||||||
pytestmark = pytest.mark.filterwarnings('error')
|
pytestmark = pytest.mark.filterwarnings("error")
|
||||||
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
|
@ -150,19 +150,11 @@ it in your setuptools-invocation:
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="myproject",
|
name="myproject",
|
||||||
packages = ['myproject'],
|
packages=["myproject"],
|
||||||
|
|
||||||
# the following makes a plugin available to pytest
|
# the following makes a plugin available to pytest
|
||||||
entry_points = {
|
entry_points={"pytest11": ["name_of_plugin = myproject.pluginmodule"]},
|
||||||
'pytest11': [
|
|
||||||
'name_of_plugin = myproject.pluginmodule',
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
# custom PyPI classifier for pytest plugins
|
# custom PyPI classifier for pytest plugins
|
||||||
classifiers=[
|
classifiers=["Framework :: Pytest"],
|
||||||
"Framework :: Pytest",
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
If a package is installed this way, ``pytest`` will load
|
If a package is installed this way, ``pytest`` will load
|
||||||
|
@ -213,11 +205,7 @@ With the following typical ``setup.py`` extract:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
setup(
|
setup(..., entry_points={"pytest11": ["foo = pytest_foo.plugin"]}, ...)
|
||||||
...,
|
|
||||||
entry_points={'pytest11': ['foo = pytest_foo.plugin']},
|
|
||||||
...,
|
|
||||||
)
|
|
||||||
|
|
||||||
In this case only ``pytest_foo/plugin.py`` will be rewritten. If the
|
In this case only ``pytest_foo/plugin.py`` will be rewritten. If the
|
||||||
helper module also contains assert statements which need to be
|
helper module also contains assert statements which need to be
|
||||||
|
@ -232,7 +220,7 @@ import ``helper.py`` normally. The contents of
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
pytest.register_assert_rewrite('pytest_foo.helper')
|
pytest.register_assert_rewrite("pytest_foo.helper")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -332,23 +320,25 @@ string value of ``Hello World!`` if we do not supply a value or ``Hello
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
group = parser.getgroup('helloworld')
|
group = parser.getgroup("helloworld")
|
||||||
group.addoption(
|
group.addoption(
|
||||||
'--name',
|
"--name",
|
||||||
action='store',
|
action="store",
|
||||||
dest='name',
|
dest="name",
|
||||||
default='World',
|
default="World",
|
||||||
help='Default "name" for hello().'
|
help='Default "name" for hello().',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def hello(request):
|
def hello(request):
|
||||||
name = request.config.getoption('name')
|
name = request.config.getoption("name")
|
||||||
|
|
||||||
def _hello(name=None):
|
def _hello(name=None):
|
||||||
if not name:
|
if not name:
|
||||||
name = request.config.getoption('name')
|
name = request.config.getoption("name")
|
||||||
return "Hello {name}!".format(name=name)
|
return "Hello {name}!".format(name=name)
|
||||||
|
|
||||||
return _hello
|
return _hello
|
||||||
|
@ -364,7 +354,8 @@ return a result object, with which we can assert the tests' outcomes.
|
||||||
"""Make sure that our plugin works."""
|
"""Make sure that our plugin works."""
|
||||||
|
|
||||||
# create a temporary conftest.py file
|
# create a temporary conftest.py file
|
||||||
testdir.makeconftest("""
|
testdir.makeconftest(
|
||||||
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.fixture(params=[
|
@pytest.fixture(params=[
|
||||||
|
@ -374,16 +365,19 @@ return a result object, with which we can assert the tests' outcomes.
|
||||||
])
|
])
|
||||||
def name(request):
|
def name(request):
|
||||||
return request.param
|
return request.param
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
# create a temporary pytest test file
|
# create a temporary pytest test file
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile(
|
||||||
|
"""
|
||||||
def test_hello_default(hello):
|
def test_hello_default(hello):
|
||||||
assert hello() == "Hello World!"
|
assert hello() == "Hello World!"
|
||||||
|
|
||||||
def test_hello_name(hello, name):
|
def test_hello_name(hello, name):
|
||||||
assert hello(name) == "Hello {0}!".format(name)
|
assert hello(name) == "Hello {0}!".format(name)
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
# run all tests with pytest
|
# run all tests with pytest
|
||||||
result = testdir.runpytest()
|
result = testdir.runpytest()
|
||||||
|
@ -514,12 +508,14 @@ after others, i.e. the position in the ``N``-sized list of functions:
|
||||||
# will execute as early as possible
|
# will execute as early as possible
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
# Plugin 2
|
# Plugin 2
|
||||||
@pytest.hookimpl(trylast=True)
|
@pytest.hookimpl(trylast=True)
|
||||||
def pytest_collection_modifyitems(items):
|
def pytest_collection_modifyitems(items):
|
||||||
# will execute as late as possible
|
# will execute as late as possible
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
# Plugin 3
|
# Plugin 3
|
||||||
@pytest.hookimpl(hookwrapper=True)
|
@pytest.hookimpl(hookwrapper=True)
|
||||||
def pytest_collection_modifyitems(items):
|
def pytest_collection_modifyitems(items):
|
||||||
|
|
Loading…
Reference in New Issue