Merge pull request #2613 from nicoddemus/features
Merge master into features
This commit is contained in:
commit
ddf1751e6d
1
AUTHORS
1
AUTHORS
|
@ -120,6 +120,7 @@ Michael Birtwell
|
||||||
Michael Droettboom
|
Michael Droettboom
|
||||||
Michael Seifert
|
Michael Seifert
|
||||||
Michal Wajszczuk
|
Michal Wajszczuk
|
||||||
|
Mihai Capotă
|
||||||
Mike Lundy
|
Mike Lundy
|
||||||
Nathaniel Waisbrot
|
Nathaniel Waisbrot
|
||||||
Ned Batchelder
|
Ned Batchelder
|
||||||
|
|
|
@ -34,13 +34,13 @@ If you are reporting a bug, please include:
|
||||||
|
|
||||||
* Your operating system name and version.
|
* Your operating system name and version.
|
||||||
* Any details about your local setup that might be helpful in troubleshooting,
|
* Any details about your local setup that might be helpful in troubleshooting,
|
||||||
specifically Python interpreter version,
|
specifically the Python interpreter version, installed libraries, and pytest
|
||||||
installed libraries and pytest version.
|
version.
|
||||||
* Detailed steps to reproduce the bug.
|
* Detailed steps to reproduce the bug.
|
||||||
|
|
||||||
If you can write a demonstration test that currently fails but should pass (xfail),
|
If you can write a demonstration test that currently fails but should pass
|
||||||
that is a very useful commit to make as well, even if you can't find how
|
(xfail), that is a very useful commit to make as well, even if you cannot
|
||||||
to fix the bug yet.
|
fix the bug itself.
|
||||||
|
|
||||||
|
|
||||||
.. _fixbugs:
|
.. _fixbugs:
|
||||||
|
@ -158,19 +158,40 @@ As stated, the objective is to share maintenance and avoid "plugin-abandon".
|
||||||
.. _`pull requests`:
|
.. _`pull requests`:
|
||||||
.. _pull-requests:
|
.. _pull-requests:
|
||||||
|
|
||||||
Preparing Pull Requests on GitHub
|
Preparing Pull Requests
|
||||||
---------------------------------
|
-----------------------
|
||||||
|
|
||||||
.. note::
|
Short version
|
||||||
What is a "pull request"? It informs project's core developers about the
|
~~~~~~~~~~~~~
|
||||||
changes you want to review and merge. Pull requests are stored on
|
|
||||||
`GitHub servers <https://github.com/pytest-dev/pytest/pulls>`_.
|
|
||||||
Once you send a pull request, we can discuss its potential modifications and
|
|
||||||
even add more commits to it later on.
|
|
||||||
|
|
||||||
There's an excellent tutorial on how Pull Requests work in the
|
#. Fork the repository;
|
||||||
`GitHub Help Center <https://help.github.com/articles/using-pull-requests/>`_,
|
#. Target ``master`` for bugfixes and doc changes;
|
||||||
but here is a simple overview:
|
#. Target ``features`` for new features or functionality changes.
|
||||||
|
#. Follow **PEP-8**. There's a ``tox`` command to help fixing it: ``tox -e fix-lint``.
|
||||||
|
#. Tests are run using ``tox``::
|
||||||
|
|
||||||
|
tox -e linting,py27,py36
|
||||||
|
|
||||||
|
The test environments above are usually enough to cover most cases locally.
|
||||||
|
|
||||||
|
#. Write a ``changelog`` entry: ``changelog/2574.bugfix``, use issue id number
|
||||||
|
and one of ``bugfix``, ``removal``, ``feature``, ``vendor``, ``doc`` or
|
||||||
|
``trivial`` for the issue type.
|
||||||
|
#. Add yourself to ``AUTHORS`` file if not there yet, in alphabetical order.
|
||||||
|
|
||||||
|
|
||||||
|
Long version
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
What is a "pull request"? It informs the project's core developers about the
|
||||||
|
changes you want to review and merge. Pull requests are stored on
|
||||||
|
`GitHub servers <https://github.com/pytest-dev/pytest/pulls>`_.
|
||||||
|
Once you send a pull request, we can discuss its potential modifications and
|
||||||
|
even add more commits to it later on. There's an excellent tutorial on how Pull
|
||||||
|
Requests work in the
|
||||||
|
`GitHub Help Center <https://help.github.com/articles/using-pull-requests/>`_.
|
||||||
|
|
||||||
|
Here is a simple overview, with pytest-specific bits:
|
||||||
|
|
||||||
#. Fork the
|
#. Fork the
|
||||||
`pytest GitHub repository <https://github.com/pytest-dev/pytest>`__. It's
|
`pytest GitHub repository <https://github.com/pytest-dev/pytest>`__. It's
|
||||||
|
@ -214,12 +235,18 @@ but here is a simple overview:
|
||||||
This command will run tests via the "tox" tool against Python 2.7 and 3.6
|
This command will run tests via the "tox" tool against Python 2.7 and 3.6
|
||||||
and also perform "lint" coding-style checks.
|
and also perform "lint" coding-style checks.
|
||||||
|
|
||||||
#. You can now edit your local working copy.
|
#. You can now edit your local working copy. Please follow PEP-8.
|
||||||
|
|
||||||
You can now make the changes you want and run the tests again as necessary.
|
You can now make the changes you want and run the tests again as necessary.
|
||||||
|
|
||||||
To run tests on Python 2.7 and pass options to pytest (e.g. enter pdb on
|
If you have too much linting errors, try running::
|
||||||
failure) to pytest you can do::
|
|
||||||
|
$ tox -e fix-lint
|
||||||
|
|
||||||
|
To fix pep8 related errors.
|
||||||
|
|
||||||
|
You can pass different options to ``tox``. For example, to run tests on Python 2.7 and pass options to pytest
|
||||||
|
(e.g. enter pdb on failure) to pytest you can do::
|
||||||
|
|
||||||
$ tox -e py27 -- --pdb
|
$ tox -e py27 -- --pdb
|
||||||
|
|
||||||
|
@ -232,9 +259,11 @@ but here is a simple overview:
|
||||||
$ git commit -a -m "<commit message>"
|
$ git commit -a -m "<commit message>"
|
||||||
$ git push -u
|
$ git push -u
|
||||||
|
|
||||||
Make sure you add a message to ``CHANGELOG.rst`` and add yourself to
|
#. Create a new changelog entry in ``changelog``. The file should be named ``<issueid>.<type>``,
|
||||||
``AUTHORS``. If you are unsure about either of these steps, submit your
|
where *issueid* is the number of the issue related to the change and *type* is one of
|
||||||
pull request and we'll help you fix it up.
|
``bugfix``, ``removal``, ``feature``, ``vendor``, ``doc`` or ``trivial``.
|
||||||
|
|
||||||
|
#. Add yourself to ``AUTHORS`` file if not there yet, in alphabetical order.
|
||||||
|
|
||||||
#. Finally, submit a pull request through the GitHub website using this data::
|
#. Finally, submit a pull request through the GitHub website using this data::
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ def pytest_load_initial_conftests(early_config, parser, args):
|
||||||
ns = early_config.known_args_namespace
|
ns = early_config.known_args_namespace
|
||||||
if ns.capture == "fd":
|
if ns.capture == "fd":
|
||||||
_py36_windowsconsoleio_workaround()
|
_py36_windowsconsoleio_workaround()
|
||||||
|
_colorama_workaround()
|
||||||
_readline_workaround()
|
_readline_workaround()
|
||||||
pluginmanager = early_config.pluginmanager
|
pluginmanager = early_config.pluginmanager
|
||||||
capman = CaptureManager(ns.capture)
|
capman = CaptureManager(ns.capture)
|
||||||
|
@ -253,6 +254,11 @@ class EncodedFile(object):
|
||||||
data = ''.join(linelist)
|
data = ''.join(linelist)
|
||||||
self.write(data)
|
self.write(data)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Ensure that file.name is a string."""
|
||||||
|
return repr(self.buffer)
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
return getattr(object.__getattribute__(self, "buffer"), name)
|
return getattr(object.__getattribute__(self, "buffer"), name)
|
||||||
|
|
||||||
|
@ -473,6 +479,24 @@ class DontReadFromInput:
|
||||||
raise AttributeError('redirected stdin has no attribute buffer')
|
raise AttributeError('redirected stdin has no attribute buffer')
|
||||||
|
|
||||||
|
|
||||||
|
def _colorama_workaround():
|
||||||
|
"""
|
||||||
|
Ensure colorama is imported so that it attaches to the correct stdio
|
||||||
|
handles on Windows.
|
||||||
|
|
||||||
|
colorama uses the terminal on import time. So if something does the
|
||||||
|
first import of colorama while I/O capture is active, colorama will
|
||||||
|
fail in various ways.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not sys.platform.startswith('win32'):
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
import colorama # noqa
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def _readline_workaround():
|
def _readline_workaround():
|
||||||
"""
|
"""
|
||||||
Ensure readline is imported so that it attaches to the correct stdio
|
Ensure readline is imported so that it attaches to the correct stdio
|
||||||
|
|
|
@ -1079,7 +1079,6 @@ class Config(object):
|
||||||
self.pluginmanager.load_setuptools_entrypoints('pytest11')
|
self.pluginmanager.load_setuptools_entrypoints('pytest11')
|
||||||
self.pluginmanager.consider_env()
|
self.pluginmanager.consider_env()
|
||||||
self.known_args_namespace = ns = self._parser.parse_known_args(args, namespace=self.option.copy())
|
self.known_args_namespace = ns = self._parser.parse_known_args(args, namespace=self.option.copy())
|
||||||
confcutdir = self.known_args_namespace.confcutdir
|
|
||||||
if self.known_args_namespace.confcutdir is None and self.inifile:
|
if self.known_args_namespace.confcutdir is None and self.inifile:
|
||||||
confcutdir = py.path.local(self.inifile).dirname
|
confcutdir = py.path.local(self.inifile).dirname
|
||||||
self.known_args_namespace.confcutdir = confcutdir
|
self.known_args_namespace.confcutdir = confcutdir
|
||||||
|
|
|
@ -140,7 +140,7 @@ class DoctestItem(pytest.Item):
|
||||||
return super(DoctestItem, self).repr_failure(excinfo)
|
return super(DoctestItem, self).repr_failure(excinfo)
|
||||||
|
|
||||||
def reportinfo(self):
|
def reportinfo(self):
|
||||||
return self.fspath, None, "[doctest] %s" % self.name
|
return self.fspath, self.dtest.lineno, "[doctest] %s" % self.name
|
||||||
|
|
||||||
|
|
||||||
def _get_flag_lookup():
|
def _get_flag_lookup():
|
||||||
|
|
|
@ -916,8 +916,11 @@ class Testdir:
|
||||||
env['PYTHONPATH'] = os.pathsep.join(filter(None, [
|
env['PYTHONPATH'] = os.pathsep.join(filter(None, [
|
||||||
str(os.getcwd()), env.get('PYTHONPATH', '')]))
|
str(os.getcwd()), env.get('PYTHONPATH', '')]))
|
||||||
kw['env'] = env
|
kw['env'] = env
|
||||||
return subprocess.Popen(cmdargs,
|
|
||||||
stdout=stdout, stderr=stderr, **kw)
|
popen = subprocess.Popen(cmdargs, stdin=subprocess.PIPE, stdout=stdout, stderr=stderr, **kw)
|
||||||
|
popen.stdin.close()
|
||||||
|
|
||||||
|
return popen
|
||||||
|
|
||||||
def run(self, *cmdargs):
|
def run(self, *cmdargs):
|
||||||
"""Run a command with arguments.
|
"""Run a command with arguments.
|
||||||
|
|
|
@ -425,9 +425,10 @@ class Module(main.File, PyCollector):
|
||||||
if e.allow_module_level:
|
if e.allow_module_level:
|
||||||
raise
|
raise
|
||||||
raise self.CollectError(
|
raise self.CollectError(
|
||||||
"Using pytest.skip outside of a test is not allowed. If you are "
|
"Using pytest.skip outside of a test is not allowed. "
|
||||||
"trying to decorate a test function, use the @pytest.mark.skip "
|
"To decorate a test function, use the @pytest.mark.skip "
|
||||||
"or @pytest.mark.skipif decorators instead."
|
"or @pytest.mark.skipif decorators instead, and to skip a "
|
||||||
|
"module use `pytestmark = pytest.mark.{skip,skipif}."
|
||||||
)
|
)
|
||||||
self.config.pluginmanager.consider_module(mod)
|
self.config.pluginmanager.consider_module(mod)
|
||||||
return mod
|
return mod
|
||||||
|
|
|
@ -382,4 +382,4 @@ def show_skipped(terminalreporter, lines):
|
||||||
reason = reason[9:]
|
reason = reason[9:]
|
||||||
lines.append(
|
lines.append(
|
||||||
"SKIP [%d] %s:%d: %s" %
|
"SKIP [%d] %s:%d: %s" %
|
||||||
(num, fspath, lineno, reason))
|
(num, fspath, lineno + 1, reason))
|
||||||
|
|
|
@ -39,8 +39,9 @@ def pytest_addoption(parser):
|
||||||
'-W', '--pythonwarnings', action='append',
|
'-W', '--pythonwarnings', action='append',
|
||||||
help="set which warnings to report, see -W option of python itself.")
|
help="set which warnings to report, see -W option of python itself.")
|
||||||
parser.addini("filterwarnings", type="linelist",
|
parser.addini("filterwarnings", type="linelist",
|
||||||
help="Each line specifies warning filter pattern which would be passed"
|
help="Each line specifies a pattern for "
|
||||||
"to warnings.filterwarnings. Process after -W and --pythonwarnings.")
|
"warnings.filterwarnings. "
|
||||||
|
"Processed after -W and --pythonwarnings.")
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Set ``stdin`` to a closed ``PIPE`` in ``pytester.py.Testdir.popen()`` for avoid unwanted interactive ``pdb``
|
|
@ -0,0 +1 @@
|
||||||
|
Add missing ``encoding`` attribute to ``sys.std*`` streams when using ``capsys`` capture mode.
|
|
@ -1 +0,0 @@
|
||||||
Provides encoding attribute on CaptureIO.
|
|
|
@ -0,0 +1 @@
|
||||||
|
Fix terminal color changing to black on Windows if ``colorama`` is imported in a ``conftest.py`` file.
|
|
@ -1 +1 @@
|
||||||
Renamed the utility function `_pytest.compat._escape_strings` to `_ascii_escaped` to better communicate the function's purpose.
|
Renamed the utility function ``_pytest.compat._escape_strings`` to ``_ascii_escaped`` to better communicate the function's purpose.
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Improve error message for CollectError with skip/skipif.
|
|
@ -0,0 +1 @@
|
||||||
|
Fix line number when reporting summary of skipped tests.
|
|
@ -0,0 +1 @@
|
||||||
|
capture: ensure that EncodedFile.name is a string.
|
|
@ -1 +1 @@
|
||||||
Emit yield test warning only once per generator
|
Emit warning about ``yield`` tests being deprecated only once per generator.
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
The options --fixtures and --fixtures-per-test will now keep indentation within docstrings.
|
The options ```--fixtures`` and ```--fixtures-per-test`` will now keep indentation within docstrings.
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Fixed all flake8 errors and warnings
|
Fixed all flake8 errors and warnings.
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Added ``fix-lint`` tox environment to run automatic pep8 fixes on the code.
|
|
@ -0,0 +1 @@
|
||||||
|
doctests line numbers are now reported correctly, fixing `pytest-sugar#122 <https://github.com/Frozenball/pytest-sugar/issues/122>`_.
|
|
@ -0,0 +1 @@
|
||||||
|
Show multiple issue links in CHANGELOG entries.
|
|
@ -13,7 +13,8 @@
|
||||||
|
|
||||||
{% if definitions[category]['showcontent'] %}
|
{% if definitions[category]['showcontent'] %}
|
||||||
{% for text, values in sections[section][category]|dictsort(by='value') %}
|
{% for text, values in sections[section][category]|dictsort(by='value') %}
|
||||||
- {{ text }}{% if category != 'vendor' %} (`{{ values[0] }} <https://github.com/pytest-dev/pytest/issues/{{ values[0][1:] }}>`_){% endif %}
|
{% set issue_joiner = joiner(', ') %}
|
||||||
|
- {{ text }}{% if category != 'vendor' %} ({% for value in values|sort %}{{ issue_joiner() }}`{{ value }} <https://github.com/pytest-dev/pytest/issues/{{ value[1:] }}>`_{% endfor %}){% endif %}
|
||||||
|
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
.. _`cache_provider`:
|
.. _`cache_provider`:
|
||||||
|
.. _cache:
|
||||||
|
|
||||||
|
|
||||||
Cache: working with cross-testrun state
|
Cache: working with cross-testrun state
|
||||||
=======================================
|
=======================================
|
||||||
|
|
|
@ -31,9 +31,10 @@ Full pytest documentation
|
||||||
plugins
|
plugins
|
||||||
writing_plugins
|
writing_plugins
|
||||||
|
|
||||||
example/index
|
|
||||||
goodpractices
|
goodpractices
|
||||||
|
pythonpath
|
||||||
customize
|
customize
|
||||||
|
example/index
|
||||||
bash-completion
|
bash-completion
|
||||||
|
|
||||||
backwards-compatibility
|
backwards-compatibility
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
Basic test configuration
|
Configuration
|
||||||
===================================
|
=============
|
||||||
|
|
||||||
Command line options and configuration file settings
|
Command line options and configuration file settings
|
||||||
-----------------------------------------------------------------
|
-----------------------------------------------------------------
|
||||||
|
@ -15,17 +15,31 @@ which were registered by installed plugins.
|
||||||
.. _rootdir:
|
.. _rootdir:
|
||||||
.. _inifiles:
|
.. _inifiles:
|
||||||
|
|
||||||
initialization: determining rootdir and inifile
|
Initialization: determining rootdir and inifile
|
||||||
-----------------------------------------------
|
-----------------------------------------------
|
||||||
|
|
||||||
.. versionadded:: 2.7
|
.. versionadded:: 2.7
|
||||||
|
|
||||||
pytest determines a "rootdir" for each test run which depends on
|
pytest determines a ``rootdir`` for each test run which depends on
|
||||||
the command line arguments (specified test files, paths) and on
|
the command line arguments (specified test files, paths) and on
|
||||||
the existence of inifiles. The determined rootdir and ini-file are
|
the existence of *ini-files*. The determined ``rootdir`` and *ini-file* are
|
||||||
printed as part of the pytest header. The rootdir is used for constructing
|
printed as part of the pytest header during startup.
|
||||||
"nodeids" during collection and may also be used by plugins to store
|
|
||||||
project/testrun-specific information.
|
Here's a summary what ``pytest`` uses ``rootdir`` for:
|
||||||
|
|
||||||
|
* Construct *nodeids* during collection; each test is assigned
|
||||||
|
a unique *nodeid* which is rooted at the ``rootdir`` and takes in account full path,
|
||||||
|
class name, function name and parametrization (if any).
|
||||||
|
|
||||||
|
* Is used by plugins as a stable location to store project/test run specific information;
|
||||||
|
for example, the internal :ref:`cache <cache>` plugin creates a ``.cache`` subdirectory
|
||||||
|
in ``rootdir`` to store its cross-test run state.
|
||||||
|
|
||||||
|
Important to emphasize that ``rootdir`` is **NOT** used to modify ``sys.path``/``PYTHONPATH`` or
|
||||||
|
influence how modules are imported. See :ref:`pythonpath` for more details.
|
||||||
|
|
||||||
|
Finding the ``rootdir``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Here is the algorithm which finds the rootdir from ``args``:
|
Here is the algorithm which finds the rootdir from ``args``:
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
|
|
||||||
.. _examples:
|
.. _examples:
|
||||||
|
|
||||||
Usages and Examples
|
Examples and customization tricks
|
||||||
===========================================
|
=================================
|
||||||
|
|
||||||
Here is a (growing) list of examples. :ref:`Contact <contact>` us if you
|
Here is a (growing) list of examples. :ref:`Contact <contact>` us if you
|
||||||
need more examples or have questions. Also take a look at the
|
need more examples or have questions. Also take a look at the
|
||||||
|
|
|
@ -26,7 +26,7 @@ Supported nose Idioms
|
||||||
* setup and teardown at module/class/method level
|
* setup and teardown at module/class/method level
|
||||||
* SkipTest exceptions and markers
|
* SkipTest exceptions and markers
|
||||||
* setup/teardown decorators
|
* setup/teardown decorators
|
||||||
* ``yield``-based tests and their setup
|
* ``yield``-based tests and their setup (considered deprecated as of pytest 3.0)
|
||||||
* ``__test__`` attribute on modules/classes/functions
|
* ``__test__`` attribute on modules/classes/functions
|
||||||
* general usage of nose utilities
|
* general usage of nose utilities
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
.. _pythonpath:
|
||||||
|
|
||||||
|
pytest import mechanisms and ``sys.path``/``PYTHONPATH``
|
||||||
|
========================================================
|
||||||
|
|
||||||
|
Here's a list of scenarios where pytest may need to change ``sys.path`` in order
|
||||||
|
to import test modules or ``conftest.py`` files.
|
||||||
|
|
||||||
|
Test modules / ``conftest.py`` files inside packages
|
||||||
|
----------------------------------------------------
|
||||||
|
|
||||||
|
Consider this file and directory layout::
|
||||||
|
|
||||||
|
root/
|
||||||
|
|- foo/
|
||||||
|
|- __init__.py
|
||||||
|
|- conftest.py
|
||||||
|
|- bar/
|
||||||
|
|- __init__.py
|
||||||
|
|- tests/
|
||||||
|
|- __init__.py
|
||||||
|
|- test_foo.py
|
||||||
|
|
||||||
|
|
||||||
|
When executing::
|
||||||
|
|
||||||
|
pytest root/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pytest will find ``foo/bar/tests/test_foo.py`` and realize it is part of a package given that
|
||||||
|
there's an ``__init__.py`` file in the same folder. It will then search upwards until it can find the
|
||||||
|
last folder which still contains an ``__init__.py`` file in order to find the package *root* (in
|
||||||
|
this case ``foo/``). To load the module, it will insert ``root/`` to the front of
|
||||||
|
``sys.path`` (if not there already) in order to load
|
||||||
|
``test_foo.py`` as the *module* ``foo.bar.tests.test_foo``.
|
||||||
|
|
||||||
|
The same logic applies to the ``conftest.py`` file: it will be imported as ``foo.conftest`` module.
|
||||||
|
|
||||||
|
Preserving the full package name is important when tests live in a package to avoid problems
|
||||||
|
and allow test modules to have duplicated names. This is also discussed in details in
|
||||||
|
:ref:`test discovery`.
|
||||||
|
|
||||||
|
Standalone test modules / ``conftest.py`` files
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
Consider this file and directory layout::
|
||||||
|
|
||||||
|
root/
|
||||||
|
|- foo/
|
||||||
|
|- conftest.py
|
||||||
|
|- bar/
|
||||||
|
|- tests/
|
||||||
|
|- test_foo.py
|
||||||
|
|
||||||
|
|
||||||
|
When executing::
|
||||||
|
|
||||||
|
pytest root/
|
||||||
|
|
||||||
|
pytest will find ``foo/bar/tests/test_foo.py`` and realize it is NOT part of a package given that
|
||||||
|
there's no ``__init__.py`` file in the same folder. It will then add ``root/foo/bar/tests`` to
|
||||||
|
``sys.path`` in order to import ``test_foo.py`` as the *module* ``test_foo``. The same is done
|
||||||
|
with the ``conftest.py`` file by adding ``root/foo`` to ``sys.path`` to import it as ``conftest``.
|
||||||
|
|
||||||
|
For this reason this layout cannot have test modules with the same name, as they all will be
|
||||||
|
imported in the global import namespace.
|
||||||
|
|
||||||
|
This is also discussed in details in :ref:`test discovery`.
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ You can invoke testing through the Python interpreter from the command line::
|
||||||
python -m pytest [...]
|
python -m pytest [...]
|
||||||
|
|
||||||
This is almost equivalent to invoking the command line script ``pytest [...]``
|
This is almost equivalent to invoking the command line script ``pytest [...]``
|
||||||
directly, except that python will also add the current directory to ``sys.path``.
|
directly, except that Python will also add the current directory to ``sys.path``.
|
||||||
|
|
||||||
Possible exit codes
|
Possible exit codes
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
|
|
|
@ -49,7 +49,7 @@ Plugin discovery order at tool startup
|
||||||
|
|
||||||
Note that pytest does not find ``conftest.py`` files in deeper nested
|
Note that pytest does not find ``conftest.py`` files in deeper nested
|
||||||
sub directories at tool startup. It is usually a good idea to keep
|
sub directories at tool startup. It is usually a good idea to keep
|
||||||
your conftest.py file in the top level test or project root directory.
|
your ``conftest.py`` file in the top level test or project root directory.
|
||||||
|
|
||||||
* by recursively loading all plugins specified by the
|
* by recursively loading all plugins specified by the
|
||||||
``pytest_plugins`` variable in ``conftest.py`` files
|
``pytest_plugins`` variable in ``conftest.py`` files
|
||||||
|
@ -94,10 +94,12 @@ Here is how you might run it::
|
||||||
If you have ``conftest.py`` files which do not reside in a
|
If you have ``conftest.py`` files which do not reside in a
|
||||||
python package directory (i.e. one containing an ``__init__.py``) then
|
python package directory (i.e. one containing an ``__init__.py``) then
|
||||||
"import conftest" can be ambiguous because there might be other
|
"import conftest" can be ambiguous because there might be other
|
||||||
``conftest.py`` files as well on your PYTHONPATH or ``sys.path``.
|
``conftest.py`` files as well on your ``PYTHONPATH`` or ``sys.path``.
|
||||||
It is thus good practice for projects to either put ``conftest.py``
|
It is thus good practice for projects to either put ``conftest.py``
|
||||||
under a package scope or to never import anything from a
|
under a package scope or to never import anything from a
|
||||||
conftest.py file.
|
``conftest.py`` file.
|
||||||
|
|
||||||
|
See also: :ref:`pythonpath`.
|
||||||
|
|
||||||
|
|
||||||
Writing your own plugin
|
Writing your own plugin
|
||||||
|
|
|
@ -335,21 +335,6 @@ def test_getstartingblock_singleline():
|
||||||
assert len(l) == 1
|
assert len(l) == 1
|
||||||
|
|
||||||
|
|
||||||
def test_getstartingblock_multiline():
|
|
||||||
class A(object):
|
|
||||||
def __init__(self, *args):
|
|
||||||
frame = sys._getframe(1)
|
|
||||||
self.source = _pytest._code.Frame(frame).statement
|
|
||||||
|
|
||||||
x = A('x',
|
|
||||||
'y'
|
|
||||||
,
|
|
||||||
'z')
|
|
||||||
|
|
||||||
l = [i for i in x.source.lines if i.strip()]
|
|
||||||
assert len(l) == 4
|
|
||||||
|
|
||||||
|
|
||||||
def test_getline_finally():
|
def test_getline_finally():
|
||||||
def c(): pass
|
def c(): pass
|
||||||
excinfo = pytest.raises(TypeError, """
|
excinfo = pytest.raises(TypeError, """
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
# flake8: noqa
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import _pytest._code
|
||||||
|
|
||||||
|
|
||||||
|
def test_getstartingblock_multiline():
|
||||||
|
"""
|
||||||
|
This test was originally found in test_source.py, but it depends on the weird
|
||||||
|
formatting of the ``x = A`` construct seen here and our autopep8 tool can only exclude entire
|
||||||
|
files (it does not support excluding lines/blocks using the traditional #noqa comment yet,
|
||||||
|
see hhatto/autopep8#307). It was considered better to just move this single test to its own
|
||||||
|
file and exclude it from autopep8 than try to complicate things.
|
||||||
|
"""
|
||||||
|
class A(object):
|
||||||
|
def __init__(self, *args):
|
||||||
|
frame = sys._getframe(1)
|
||||||
|
self.source = _pytest._code.Frame(frame).statement
|
||||||
|
|
||||||
|
x = A('x',
|
||||||
|
'y'
|
||||||
|
,
|
||||||
|
'z')
|
||||||
|
|
||||||
|
l = [i for i in x.source.lines if i.strip()]
|
||||||
|
assert len(l) == 4
|
|
@ -288,7 +288,7 @@ class TestLoggingInteraction(object):
|
||||||
stream.close() # to free memory/release resources
|
stream.close() # to free memory/release resources
|
||||||
""")
|
""")
|
||||||
result = testdir.runpytest_subprocess(p)
|
result = testdir.runpytest_subprocess(p)
|
||||||
result.stderr.str().find("atexit") == -1
|
assert result.stderr.str().find("atexit") == -1
|
||||||
|
|
||||||
def test_logging_and_immediate_setupteardown(self, testdir):
|
def test_logging_and_immediate_setupteardown(self, testdir):
|
||||||
p = testdir.makepyfile("""
|
p = testdir.makepyfile("""
|
||||||
|
@ -716,13 +716,21 @@ def test_dupfile(tmpfile):
|
||||||
assert nf not in flist
|
assert nf not in flist
|
||||||
print(i, end="", file=nf)
|
print(i, end="", file=nf)
|
||||||
flist.append(nf)
|
flist.append(nf)
|
||||||
|
|
||||||
|
fname_open = flist[0].name
|
||||||
|
assert fname_open == repr(flist[0].buffer)
|
||||||
|
|
||||||
for i in range(5):
|
for i in range(5):
|
||||||
f = flist[i]
|
f = flist[i]
|
||||||
f.close()
|
f.close()
|
||||||
|
fname_closed = flist[0].name
|
||||||
|
assert fname_closed == repr(flist[0].buffer)
|
||||||
|
assert fname_closed != fname_open
|
||||||
tmpfile.seek(0)
|
tmpfile.seek(0)
|
||||||
s = tmpfile.read()
|
s = tmpfile.read()
|
||||||
assert "01234" in repr(s)
|
assert "01234" in repr(s)
|
||||||
tmpfile.close()
|
tmpfile.close()
|
||||||
|
assert fname_closed == repr(flist[0].buffer)
|
||||||
|
|
||||||
|
|
||||||
def test_dupfile_on_bytesio():
|
def test_dupfile_on_bytesio():
|
||||||
|
@ -730,6 +738,7 @@ def test_dupfile_on_bytesio():
|
||||||
f = capture.safe_text_dupfile(io, "wb")
|
f = capture.safe_text_dupfile(io, "wb")
|
||||||
f.write("hello")
|
f.write("hello")
|
||||||
assert io.getvalue() == b"hello"
|
assert io.getvalue() == b"hello"
|
||||||
|
assert 'BytesIO object' in f.name
|
||||||
|
|
||||||
|
|
||||||
def test_dupfile_on_textio():
|
def test_dupfile_on_textio():
|
||||||
|
@ -737,6 +746,7 @@ def test_dupfile_on_textio():
|
||||||
f = capture.safe_text_dupfile(io, "wb")
|
f = capture.safe_text_dupfile(io, "wb")
|
||||||
f.write("hello")
|
f.write("hello")
|
||||||
assert io.getvalue() == "hello"
|
assert io.getvalue() == "hello"
|
||||||
|
assert not hasattr(f, 'name')
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
|
|
|
@ -545,6 +545,22 @@ class TestDoctests(object):
|
||||||
result = testdir.runpytest(p, '--doctest-modules')
|
result = testdir.runpytest(p, '--doctest-modules')
|
||||||
result.stdout.fnmatch_lines(['* 1 passed *'])
|
result.stdout.fnmatch_lines(['* 1 passed *'])
|
||||||
|
|
||||||
|
def test_reportinfo(self, testdir):
|
||||||
|
'''
|
||||||
|
Test case to make sure that DoctestItem.reportinfo() returns lineno.
|
||||||
|
'''
|
||||||
|
p = testdir.makepyfile(test_reportinfo="""
|
||||||
|
def foo(x):
|
||||||
|
'''
|
||||||
|
>>> foo('a')
|
||||||
|
'b'
|
||||||
|
'''
|
||||||
|
return 'c'
|
||||||
|
""")
|
||||||
|
items, reprec = testdir.inline_genitems(p, '--doctest-modules')
|
||||||
|
reportinfo = items[0].reportinfo()
|
||||||
|
assert reportinfo[1] == 1
|
||||||
|
|
||||||
|
|
||||||
class TestLiterals(object):
|
class TestLiterals(object):
|
||||||
|
|
||||||
|
|
|
@ -708,7 +708,7 @@ def test_skipped_reasons_functional(testdir):
|
||||||
)
|
)
|
||||||
result = testdir.runpytest('-rs')
|
result = testdir.runpytest('-rs')
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"*SKIP*2*conftest.py:3: test",
|
"*SKIP*2*conftest.py:4: test",
|
||||||
])
|
])
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
|
||||||
|
|
8
tox.ini
8
tox.ini
|
@ -156,6 +156,14 @@ commands =
|
||||||
rm -rf /tmp/doc-exec*
|
rm -rf /tmp/doc-exec*
|
||||||
make regen
|
make regen
|
||||||
|
|
||||||
|
[testenv:fix-lint]
|
||||||
|
skipsdist = True
|
||||||
|
usedevelop = True
|
||||||
|
deps =
|
||||||
|
autopep8
|
||||||
|
commands =
|
||||||
|
autopep8 --in-place -r --max-line-length=120 --exclude=vendored_packages,test_source_multiline_block.py _pytest testing
|
||||||
|
|
||||||
[testenv:jython]
|
[testenv:jython]
|
||||||
changedir = testing
|
changedir = testing
|
||||||
commands =
|
commands =
|
||||||
|
|
Loading…
Reference in New Issue