Merge pull request #2389 from nicoddemus/merge-master-into-features
Merge master into features
This commit is contained in:
		
						commit
						daca618012
					
				|  | @ -31,7 +31,7 @@ env: | |||
| matrix: | ||||
|   include: | ||||
|     - env: TOXENV=py36 | ||||
|       python: '3.6-dev' | ||||
|       python: '3.6' | ||||
|     - env: TOXENV=py37 | ||||
|       python: 'nightly' | ||||
|   allow_failures: | ||||
|  |  | |||
							
								
								
									
										5
									
								
								AUTHORS
								
								
								
								
							
							
						
						
									
										5
									
								
								AUTHORS
								
								
								
								
							|  | @ -6,6 +6,7 @@ Contributors include:: | |||
| Abdeali JK | ||||
| Abhijeet Kasurde | ||||
| Ahn Ki-Wook | ||||
| Alexander Johnson | ||||
| Alexei Kozlenok | ||||
| Anatoly Bubenkoff | ||||
| Andreas Zeidler | ||||
|  | @ -86,6 +87,7 @@ Justyna Janczyszyn | |||
| Kale Kundert | ||||
| Katarzyna Jachim | ||||
| Kevin Cox | ||||
| Kodi B. Arfer | ||||
| Lee Kamentsky | ||||
| Lev Maximov | ||||
| Loic Esteve | ||||
|  | @ -122,6 +124,7 @@ Oliver Bestwalter | |||
| Omar Kohl | ||||
| Omer Hadari | ||||
| Patrick Hayes | ||||
| Paweł Adamczak | ||||
| Pieter Mulder | ||||
| Piotr Banaszkiewicz | ||||
| Punyashloka Biswal | ||||
|  | @ -140,6 +143,7 @@ Russel Winder | |||
| Ryan Wooden | ||||
| Samuele Pedroni | ||||
| Simon Gomizelj | ||||
| Skylar Downes | ||||
| Stefan Farmbauer | ||||
| Stefan Zimmermann | ||||
| Stefano Taschini | ||||
|  | @ -155,5 +159,6 @@ Vasily Kuznetsov | |||
| Victor Uriarte | ||||
| Vlad Dragos | ||||
| Vidar T. Fauske | ||||
| Vitaly Lashmanov | ||||
| Wouter van Ackooy | ||||
| Xuecong Liao | ||||
|  |  | |||
|  | @ -121,7 +121,22 @@ Bug Fixes | |||
| 3.0.8 (unreleased) | ||||
| ================== | ||||
| 
 | ||||
| * | ||||
| * Change capture.py's ``DontReadFromInput`` class to throw ``io.UnsupportedOperation`` errors rather | ||||
|   than ValueErrors in the ``fileno`` method (`#2276`_). | ||||
|   Thanks `@metasyn`_ for the PR. | ||||
| 
 | ||||
| * Fix exception formatting while importing modules when the exception message | ||||
|   contains non-ascii characters (`#2336`_). | ||||
|   Thanks `@fabioz`_ for the report and `@nicoddemus`_ for the PR. | ||||
|    | ||||
| * Added documentation related to issue (`#1937`_) | ||||
|   Thanks `@skylarjhdownes`_ for the PR. | ||||
| 
 | ||||
| * Allow collecting files with any file extension as Python modules (`#2369`_). | ||||
|   Thanks `@Kodiologist`_ for the PR. | ||||
| 
 | ||||
| * Show the correct error message when collect "parametrize" func with wrong args (`#2383`_). | ||||
|   Thanks `@The-Compiler`_ for the report and `@robin0371`_ for the PR. | ||||
| 
 | ||||
| * | ||||
| 
 | ||||
|  | @ -129,7 +144,20 @@ Bug Fixes | |||
| 
 | ||||
| * | ||||
| 
 | ||||
| * | ||||
| 
 | ||||
|    | ||||
| .. _@skylarjhdownes: https://github.com/skylarjhdownes | ||||
| .. _@fabioz: https://github.com/fabioz | ||||
| .. _@metasyn: https://github.com/metasyn | ||||
| .. _@Kodiologist: https://github.com/Kodiologist | ||||
| .. _@robin0371: https://github.com/robin0371 | ||||
| 
 | ||||
| 
 | ||||
| .. _#1937: https://github.com/pytest-dev/pytest/issues/1937 | ||||
| .. _#2276: https://github.com/pytest-dev/pytest/issues/2276 | ||||
| .. _#2336: https://github.com/pytest-dev/pytest/issues/2336 | ||||
| .. _#2369: https://github.com/pytest-dev/pytest/issues/2369 | ||||
| .. _#2383: https://github.com/pytest-dev/pytest/issues/2383 | ||||
| 
 | ||||
| 
 | ||||
| 3.0.7 (2017-03-14) | ||||
|  | @ -138,7 +166,7 @@ Bug Fixes | |||
| 
 | ||||
| * Fix issue in assertion rewriting breaking due to modules silently discarding | ||||
|   other modules when importing fails | ||||
|   Notably, importing the `anydbm` module is fixed. (`#2248`_). | ||||
|   Notably, importing the ``anydbm`` module is fixed. (`#2248`_). | ||||
|   Thanks `@pfhayes`_ for the PR. | ||||
| 
 | ||||
| * junitxml: Fix problematic case where system-out tag occured twice per testcase | ||||
|  | @ -401,6 +429,7 @@ Bug Fixes | |||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 3.0.2 (2016-09-01) | ||||
| ================== | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,12 +7,19 @@ | |||
| 
 | ||||
| .. image:: https://img.shields.io/pypi/v/pytest.svg | ||||
|     :target: https://pypi.python.org/pypi/pytest | ||||
| 
 | ||||
| .. image:: https://anaconda.org/conda-forge/pytest/badges/version.svg | ||||
|     :target: https://anaconda.org/conda-forge/pytest | ||||
| 
 | ||||
| .. image:: https://img.shields.io/pypi/pyversions/pytest.svg | ||||
|     :target: https://pypi.python.org/pypi/pytest | ||||
| 
 | ||||
| .. image:: https://img.shields.io/coveralls/pytest-dev/pytest/master.svg | ||||
|     :target: https://coveralls.io/r/pytest-dev/pytest | ||||
| 
 | ||||
| .. image:: https://travis-ci.org/pytest-dev/pytest.svg?branch=master | ||||
|     :target: https://travis-ci.org/pytest-dev/pytest | ||||
| 
 | ||||
| .. image:: https://ci.appveyor.com/api/projects/status/mrgbjaua7t33pg6b?svg=true | ||||
|     :target: https://ci.appveyor.com/project/pytestbot/pytest | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,13 +3,14 @@ import sys | |||
| from inspect import CO_VARARGS, CO_VARKEYWORDS | ||||
| import re | ||||
| from weakref import ref | ||||
| from _pytest.compat import _PY2, _PY3, PY35 | ||||
| 
 | ||||
| import py | ||||
| builtin_repr = repr | ||||
| 
 | ||||
| reprlib = py.builtin._tryimport('repr', 'reprlib') | ||||
| 
 | ||||
| if sys.version_info[0] >= 3: | ||||
| if _PY3: | ||||
|     from traceback import format_exception_only | ||||
| else: | ||||
|     from ._py2traceback import format_exception_only | ||||
|  | @ -353,7 +354,7 @@ class ExceptionInfo(object): | |||
|         help for navigating the traceback. | ||||
|     """ | ||||
|     _striptext = '' | ||||
|     _assert_start_repr = "AssertionError(u\'assert " if sys.version_info[0] < 3 else "AssertionError(\'assert " | ||||
|     _assert_start_repr = "AssertionError(u\'assert " if _PY2 else "AssertionError(\'assert " | ||||
| 
 | ||||
|     def __init__(self, tup=None, exprinfo=None): | ||||
|         import _pytest._code | ||||
|  | @ -618,7 +619,7 @@ class FormattedExcinfo(object): | |||
| 
 | ||||
| 
 | ||||
|     def repr_excinfo(self, excinfo): | ||||
|         if sys.version_info[0] < 3: | ||||
|         if _PY2: | ||||
|             reprtraceback = self.repr_traceback(excinfo) | ||||
|             reprcrash = excinfo._getreprcrash() | ||||
| 
 | ||||
|  | @ -655,7 +656,7 @@ class FormattedExcinfo(object): | |||
| class TerminalRepr(object): | ||||
|     def __str__(self): | ||||
|         s = self.__unicode__() | ||||
|         if sys.version_info[0] < 3: | ||||
|         if _PY2: | ||||
|             s = s.encode('utf-8') | ||||
|         return s | ||||
| 
 | ||||
|  | @ -851,7 +852,7 @@ def getrawcode(obj, trycall=True): | |||
|         return obj | ||||
| 
 | ||||
| 
 | ||||
| if sys.version_info[:2] >= (3, 5):  # RecursionError introduced in 3.5 | ||||
| if PY35:  # RecursionError introduced in 3.5 | ||||
|     def is_recursion_error(excinfo): | ||||
|         return excinfo.errisinstance(RecursionError)  # noqa | ||||
| else: | ||||
|  |  | |||
|  | @ -28,6 +28,7 @@ _PY2 = not _PY3 | |||
| NoneType = type(None) | ||||
| NOTSET = object() | ||||
| 
 | ||||
| PY35 = sys.version_info[:2] >= (3, 5) | ||||
| PY36 = sys.version_info[:2] >= (3, 6) | ||||
| MODULE_NOT_FOUND_ERROR = 'ModuleNotFoundError' if PY36 else 'ImportError' | ||||
| 
 | ||||
|  | @ -250,8 +251,10 @@ else: | |||
|         try: | ||||
|             return str(v) | ||||
|         except UnicodeError: | ||||
|             if not isinstance(v, unicode): | ||||
|                 v = unicode(v) | ||||
|             errors = 'replace' | ||||
|             return v.encode('ascii', errors) | ||||
|             return v.encode('utf-8', errors) | ||||
| 
 | ||||
| 
 | ||||
| COLLECT_FAKEMODULE_ATTRIBUTES = ( | ||||
|  |  | |||
|  | @ -826,8 +826,8 @@ class FixtureFunctionMarker(object): | |||
| def fixture(scope="function", params=None, autouse=False, ids=None, name=None): | ||||
|     """ (return a) decorator to mark a fixture factory function. | ||||
| 
 | ||||
|     This decorator can be used (with or or without parameters) to define | ||||
|     a fixture function.  The name of the fixture function can later be | ||||
|     This decorator can be used (with or without parameters) to define a | ||||
|     fixture function.  The name of the fixture function can later be | ||||
|     referenced to cause its invocation ahead of running tests: test | ||||
|     modules or classes can use the pytest.mark.usefixtures(fixturename) | ||||
|     marker.  Test functions can directly use fixture names as input | ||||
|  |  | |||
|  | @ -452,9 +452,10 @@ class Testdir(object): | |||
|         the module is re-imported. | ||||
|         """ | ||||
|         for name in set(sys.modules).difference(self._savemodulekeys): | ||||
|             # zope.interface (used by twisted-related tests) keeps internal | ||||
|             # state and can't be deleted | ||||
|             if not name.startswith("zope.interface"): | ||||
|             # some zope modules used by twisted-related tests keeps internal | ||||
|             # state and can't be deleted; we had some trouble in the past | ||||
|             # with zope.interface for example | ||||
|             if not name.startswith("zope"): | ||||
|                 del sys.modules[name] | ||||
| 
 | ||||
|     def make_hook_recorder(self, pluginmanager): | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ from __future__ import absolute_import, division, print_function | |||
| import fnmatch | ||||
| import inspect | ||||
| import sys | ||||
| import os | ||||
| import collections | ||||
| import math | ||||
| from itertools import count | ||||
|  | @ -20,7 +21,7 @@ from _pytest.compat import ( | |||
|     isclass, isfunction, is_generator, _escape_strings, | ||||
|     REGEX_TYPE, STRING_TYPES, NoneType, NOTSET, | ||||
|     get_real_func, getfslineno, safe_getattr, | ||||
|     getlocation, enum, | ||||
|     safe_str, getlocation, enum, | ||||
| ) | ||||
| from _pytest.runner import fail | ||||
| 
 | ||||
|  | @ -223,8 +224,7 @@ class PyobjMixin(PyobjContext): | |||
|                 continue | ||||
|             name = node.name | ||||
|             if isinstance(node, Module): | ||||
|                 assert name.endswith(".py") | ||||
|                 name = name[:-3] | ||||
|                 name = os.path.splitext(name)[0] | ||||
|                 if stopatmodule: | ||||
|                     if includemodule: | ||||
|                         parts.append(name) | ||||
|  | @ -427,7 +427,7 @@ class Module(main.File, PyCollector): | |||
|             if self.config.getoption('verbose') < 2: | ||||
|                 exc_info.traceback = exc_info.traceback.filter(filter_traceback) | ||||
|             exc_repr = exc_info.getrepr(style='short') if exc_info.traceback else exc_info.exconly() | ||||
|             formatted_tb = py._builtin._totext(exc_repr) | ||||
|             formatted_tb = safe_str(exc_repr) | ||||
|             raise self.CollectError( | ||||
|                 "ImportError while importing test module '{fspath}'.\n" | ||||
|                 "Hint: make sure your test modules/packages have valid Python names.\n" | ||||
|  | @ -843,7 +843,11 @@ class Metafunc(fixtures.FuncargnamesCompatAttr): | |||
|         for callspec in self._calls or [CallSpec2(self)]: | ||||
|             elements = zip(ids, parameters, count()) | ||||
|             for a_id, param, param_index in elements: | ||||
|                 assert len(param.values) == len(argnames) | ||||
|                 if len(param.values) != len(argnames): | ||||
|                     raise ValueError( | ||||
|                         'In "parametrize" the number of values ({0}) must be ' | ||||
|                         'equal to the number of names ({1})'.format( | ||||
|                             param.values, argnames)) | ||||
|                 newcallspec = callspec.copy(self) | ||||
|                 newcallspec.setmulti(valtypes, argnames, param.values, a_id, | ||||
|                                      param.deprecated_arg_dict, scopenum, param_index) | ||||
|  |  | |||
|  | @ -270,12 +270,21 @@ supporting modules which are not themselves test modules will not be rewritten. | |||
| 
 | ||||
| .. note:: | ||||
| 
 | ||||
|    ``pytest`` rewrites test modules on import. It does this by using an import | ||||
|    hook to write new pyc files. Most of the time this works transparently. | ||||
|    ``pytest`` rewrites test modules on import by using an import | ||||
|    hook to write new ``pyc`` files. Most of the time this works transparently. | ||||
|    However, if you are messing with import yourself, the import hook may | ||||
|    interfere. If this is the case, use ``--assert=plain``. Additionally, | ||||
|    rewriting will fail silently if it cannot write new pycs, i.e. in a read-only | ||||
|    filesystem or a zipfile. | ||||
|    interfere. | ||||
| 
 | ||||
|    If this is the case you have two options: | ||||
| 
 | ||||
|    * Disable rewriting for a specific module by adding the string | ||||
|      ``PYTEST_DONT_REWRITE`` to its docstring. | ||||
| 
 | ||||
|    * Disable rewriting for all modules by using ``--assert=plain``. | ||||
| 
 | ||||
|    Additionally, rewriting will fail silently if it cannot write new ``.pyc`` files, | ||||
|    i.e. in a read-only filesystem or a zipfile. | ||||
| 
 | ||||
| 
 | ||||
| For further information, Benjamin Peterson wrote up `Behind the scenes of pytest's new assertion rewriting <http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests-new-assertion.html>`_. | ||||
| 
 | ||||
|  |  | |||
|  | @ -12,6 +12,7 @@ Full pytest documentation | |||
| 
 | ||||
|    getting-started | ||||
|    usage | ||||
|    existingtestsuite | ||||
|    assert | ||||
|    builtin | ||||
|    fixture | ||||
|  |  | |||
|  | @ -45,7 +45,7 @@ Here is the algorithm which finds the rootdir from ``args``: | |||
|   matched, it becomes the ini-file and its directory becomes the rootdir. | ||||
| 
 | ||||
| - if no ini-file was found, use the already determined common ancestor as root | ||||
|   directory. This allows to work with pytest in structures that are not part of | ||||
|   directory. This allows the use of pytest in structures that are not part of | ||||
|   a package and don't have any particular ini-file configuration. | ||||
| 
 | ||||
| If no ``args`` are given, pytest collects test below the current working | ||||
|  | @ -97,6 +97,8 @@ check for ini-files as follows:: | |||
| .. _`how to change command line options defaults`: | ||||
| .. _`adding default options`: | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| How to change command line options defaults | ||||
| ------------------------------------------------ | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,34 @@ | |||
| .. _existingtestsuite: | ||||
| 
 | ||||
| Using pytest with an existing test suite | ||||
| =========================================== | ||||
| 
 | ||||
| Pytest can be used with most existing test suites, but its | ||||
| behavior differs from other test runners such as :ref:`nose <noseintegration>` or | ||||
| Python's default unittest framework. | ||||
| 
 | ||||
| Before using this section you will want to :ref:`install pytest <getstarted>`. | ||||
| 
 | ||||
| Running an existing test suite with pytest | ||||
| --------------------------------------------- | ||||
| 
 | ||||
| Say you want to contribute to an existing repository somewhere. | ||||
| After pulling the code into your development space using some | ||||
| flavor of version control and (optionally) setting up a virtualenv | ||||
| you will want to run:: | ||||
| 
 | ||||
|     cd <repository> | ||||
|     pip install -e .  # Environment dependent alternatives include | ||||
|                       # 'python setup.py develop' and 'conda develop' | ||||
| 
 | ||||
| in your project root.  This will set up a symlink to your code in | ||||
| site-packages, allowing you to edit your code while your tests | ||||
| run against it as if it were installed. | ||||
| 
 | ||||
| Setting up your project in development mode lets you avoid having to | ||||
| reinstall every time you want to run your tests, and is less brittle than | ||||
| mucking about with sys.path to point your tests at local code. | ||||
| 
 | ||||
| Also consider using :ref:`tox <use tox>`. | ||||
| 
 | ||||
| .. include:: links.inc | ||||
|  | @ -253,7 +253,7 @@ the code after the *yield* statement serves as the teardown code: | |||
|     import pytest | ||||
| 
 | ||||
|     @pytest.fixture(scope="module") | ||||
|     def smtp(request): | ||||
|     def smtp(): | ||||
|         smtp = smtplib.SMTP("smtp.gmail.com") | ||||
|         yield smtp  # provide the fixture value | ||||
|         print("teardown smtp") | ||||
|  | @ -287,7 +287,7 @@ Note that we can also seamlessly use the ``yield`` syntax with ``with`` statemen | |||
|     import pytest | ||||
| 
 | ||||
|     @pytest.fixture(scope="module") | ||||
|     def smtp(request): | ||||
|     def smtp(): | ||||
|         with smtplib.SMTP("smtp.gmail.com") as smtp: | ||||
|             yield smtp  # provide the fixture value | ||||
| 
 | ||||
|  |  | |||
|  | @ -192,6 +192,7 @@ Here are a few suggestions where to go next: | |||
| 
 | ||||
| * :ref:`cmdline` for command line invocation examples | ||||
| * :ref:`good practices <goodpractices>` for virtualenv, test layout | ||||
| * :ref:`existingtestsuite` for working with pre-existing tests | ||||
| * :ref:`fixtures` for providing a functional baseline to your tests | ||||
| * :ref:`plugins` managing and writing plugins | ||||
| 
 | ||||
|  |  | |||
|  | @ -30,68 +30,106 @@ Within Python modules, ``pytest`` also discovers tests using the standard | |||
| 
 | ||||
| 
 | ||||
| Choosing a test layout / import rules | ||||
| ------------------------------------------ | ||||
| ------------------------------------- | ||||
| 
 | ||||
| ``pytest`` supports two common test layouts: | ||||
| 
 | ||||
| * putting tests into an extra directory outside your actual application | ||||
|   code, useful if you have many functional tests or for other reasons | ||||
|   want to keep tests separate from actual application code (often a good | ||||
|   idea):: | ||||
| Tests outside application code | ||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||||
| 
 | ||||
|     setup.py   # your setuptools Python package metadata | ||||
| Putting tests into an extra directory outside your actual application code | ||||
| might be useful if you have many functional tests or for other reasons want | ||||
| to keep tests separate from actual application code (often a good idea):: | ||||
| 
 | ||||
|     setup.py | ||||
|     mypkg/ | ||||
|         __init__.py | ||||
|         appmodule.py | ||||
|         app.py | ||||
|         view.py | ||||
|     tests/ | ||||
|         test_app.py | ||||
|         test_view.py | ||||
|         ... | ||||
| 
 | ||||
| This way your tests can run easily against an installed version | ||||
| of ``mypkg``. | ||||
| 
 | ||||
| * inlining test directories into your application package, useful if you | ||||
|   have direct relation between (unit-)test and application modules and | ||||
|   want to distribute your tests along with your application:: | ||||
| Note that using this scheme your test files must have **unique names**, because | ||||
| ``pytest`` will import them as *top-level* modules since there are no packages | ||||
| to derive a full package name from. In other words, the test files in the example above will | ||||
| be imported as ``test_app`` and ``test_view`` top-level modules by adding ``tests/`` to | ||||
| ``sys.path``. | ||||
| 
 | ||||
|     setup.py   # your setuptools Python package metadata | ||||
| If you need to have test modules with the same name, you might add ``__init__.py`` files to your | ||||
| ``tests`` folder and subfolders, changing them to packages:: | ||||
| 
 | ||||
|     setup.py | ||||
|     mypkg/ | ||||
|         ... | ||||
|     tests/ | ||||
|         __init__.py | ||||
|         foo/ | ||||
|             __init__.py | ||||
|             test_view.py | ||||
|         bar/ | ||||
|             __init__.py | ||||
|             test_view.py | ||||
| 
 | ||||
| Now pytest will load the modules as ``tests.foo.test_view`` and ``tests.bar.test_view``, allowing | ||||
| you to have modules with the same name. But now this introduces a subtle problem: in order to load | ||||
| the test modules from the ``tests`` directory, pytest prepends the root of the repository to | ||||
| ``sys.path``, which adds the side-effect that now ``mypkg`` is also importable. | ||||
| This is problematic if you are using a tool like `tox`_ to test your package in a virtual environment, | ||||
| because you want to test the *installed* version of your package, not the local code from the repository. | ||||
| 
 | ||||
| In this situation, it is **strongly** suggested to use a ``src`` layout where application root package resides in a | ||||
| sub-directory of your root:: | ||||
| 
 | ||||
|     setup.py | ||||
|     src/ | ||||
|         mypkg/ | ||||
|             __init__.py | ||||
|         appmodule.py | ||||
|         ... | ||||
|             app.py | ||||
|             view.py | ||||
|     tests/ | ||||
|         __init__.py | ||||
|         foo/ | ||||
|             __init__.py | ||||
|             test_view.py | ||||
|         bar/ | ||||
|             __init__.py | ||||
|             test_view.py | ||||
| 
 | ||||
| 
 | ||||
| This layout prevents a lot of common pitfalls and has many benefits, which are better explained in this excellent | ||||
| `blog post by Ionel Cristian Mărieș <https://blog.ionelmc.ro/2014/05/25/python-packaging/#the-structure>`_. | ||||
| 
 | ||||
| Tests as part of application code | ||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||||
| 
 | ||||
| Inlining test directories into your application package | ||||
| is useful if you have direct relation between tests and application modules and | ||||
| want to distribute them along with your application:: | ||||
| 
 | ||||
|     setup.py | ||||
|     mypkg/ | ||||
|         __init__.py | ||||
|         app.py | ||||
|         view.py | ||||
|         test/ | ||||
|             __init__.py | ||||
|             test_app.py | ||||
|             test_view.py | ||||
|             ... | ||||
| 
 | ||||
| Important notes relating to both schemes: | ||||
| In this scheme, it is easy to your run tests using the ``--pyargs`` option:: | ||||
| 
 | ||||
| - **make sure that "mypkg" is importable**, for example by typing once:: | ||||
|     pytest --pyargs mypkg | ||||
| 
 | ||||
|      pip install -e .   # install package using setup.py in editable mode | ||||
| ``pytest`` will discover where ``mypkg`` is installed and collect tests from there. | ||||
| 
 | ||||
| - **avoid "__init__.py" files in your test directories**. | ||||
|   This way your tests can run easily against an installed version | ||||
|   of ``mypkg``, independently from the installed package if it contains | ||||
|   the tests or not. | ||||
| Note that this layout also works in conjunction with the ``src`` layout mentioned in the previous section. | ||||
| 
 | ||||
| - With inlined tests you might put ``__init__.py`` into test | ||||
|   directories and make them installable as part of your application. | ||||
|   Using the ``pytest --pyargs mypkg`` invocation pytest will | ||||
|   discover where mypkg is installed and collect tests from there. | ||||
|   With the "external" test you can still distribute tests but they | ||||
|   will not be installed or become importable. | ||||
| 
 | ||||
| Typically you can run tests by pointing to test directories or modules:: | ||||
| 
 | ||||
|     pytest tests/test_app.py       # for external test dirs | ||||
|     pytest mypkg/test/test_app.py  # for inlined test dirs | ||||
|     pytest mypkg                   # run tests in all below test directories | ||||
|     pytest                         # run all tests below current dir | ||||
|     ... | ||||
| 
 | ||||
| Because of the above ``editable install`` mode you can change your | ||||
| source code (both tests and the app) and rerun tests at will. | ||||
| Once you are done with your work, you can `use tox`_ to make sure | ||||
| that the package is really correct and tests pass in all | ||||
| required configurations. | ||||
| 
 | ||||
| .. note:: | ||||
| 
 | ||||
|  | @ -144,7 +182,15 @@ for installing your application and any dependencies | |||
| as well as the ``pytest`` package itself. This ensures your code and | ||||
| dependencies are isolated from the system Python installation. | ||||
| 
 | ||||
| If you frequently release code and want to make sure that your actual | ||||
| You can then install your package in "editable" mode:: | ||||
| 
 | ||||
|      pip install -e . | ||||
| 
 | ||||
| which lets you change your source code (both tests and application) and rerun tests at will. | ||||
| This is similar to running `python setup.py develop` or `conda develop` in that it installs | ||||
| your package using a symlink to your development code. | ||||
| 
 | ||||
| Once you are done with your work and want to make sure that your actual | ||||
| package passes all tests you may want to look into `tox`_, the | ||||
| virtualenv test automation tool and its `pytest support | ||||
| <https://tox.readthedocs.io/en/latest/example/pytest.html>`_. | ||||
|  | @ -154,11 +200,6 @@ options.  It will run tests against the installed package and not | |||
| against your source code checkout, helping to detect packaging | ||||
| glitches. | ||||
| 
 | ||||
| Continuous integration services such as Jenkins_ can make use of the | ||||
| ``--junitxml=PATH`` option to create a JUnitXML file and generate reports (e.g. | ||||
| by publishing the results in a nice format with the `Jenkins xUnit Plugin | ||||
| <https://wiki.jenkins-ci.org/display/JENKINS/xUnit+Plugin>`_). | ||||
| 
 | ||||
| 
 | ||||
| Integrating with setuptools / ``python setup.py test`` / ``pytest-runner`` | ||||
| -------------------------------------------------------------------------- | ||||
|  |  | |||
|  | @ -47,9 +47,19 @@ Unsupported idioms / known issues | |||
|   ``tests.test_mod``) but different file system paths | ||||
|   (e.g. ``tests/test_mode.py`` and ``other/tests/test_mode.py``) | ||||
|   by extending sys.path/import semantics.   pytest does not do that | ||||
|   but there is discussion in `issue268 <https://github.com/pytest-dev/pytest/issues/268>`_ for adding some support.  Note that | ||||
|   but there is discussion in `#268 <https://github.com/pytest-dev/pytest/issues/268>`_ for adding some support.  Note that | ||||
|   `nose2 choose to avoid this sys.path/import hackery <https://nose2.readthedocs.io/en/latest/differences.html#test-discovery-and-loading>`_. | ||||
| 
 | ||||
|   If you place a conftest.py file in the root directory of your project | ||||
|   (as determined by pytest) pytest will run tests "nose style" against | ||||
|   the code below that directory by adding it to your ``sys.path`` instead of | ||||
|   running against your installed code. | ||||
| 
 | ||||
|   You may find yourself wanting to do this if you ran ``python setup.py install`` | ||||
|   to set up your project, as opposed to ``python setup.py develop`` or any of | ||||
|   the package manager equivalents.  Installing with develop in a | ||||
|   virtual environment like Tox is recommended over this pattern. | ||||
| 
 | ||||
| - nose-style doctests are not collected and executed correctly, | ||||
|   also doctest fixtures don't work. | ||||
| 
 | ||||
|  | @ -62,3 +72,4 @@ Unsupported idioms / known issues | |||
|   being the recommended alternative. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,3 @@ | |||
| # pinning sphinx to 1.4.* due to search issues with rtd: | ||||
| # https://github.com/rtfd/readthedocs-sphinx-ext/issues/25 | ||||
| sphinx ==1.4.* | ||||
|  | @ -19,6 +19,18 @@ You can invoke testing through the Python interpreter from the command line:: | |||
| This is almost equivalent to invoking the command line script ``pytest [...]`` | ||||
| directly, except that python will also add the current directory to ``sys.path``. | ||||
| 
 | ||||
| Possible exit codes | ||||
| -------------------------------------------------------------- | ||||
| 
 | ||||
| Running ``pytest`` can result in six different exit codes: | ||||
| 
 | ||||
| :Exit code 0: All tests were collected and passed successfully | ||||
| :Exit code 1: Tests were collected and run but some of the tests failed | ||||
| :Exit code 2: Test execution was interrupted by the user | ||||
| :Exit code 3: Internal error happened while executing tests | ||||
| :Exit code 4: pytest command line usage error | ||||
| :Exit code 5: No tests were collected | ||||
| 
 | ||||
| Getting help on version, option names, environment variables | ||||
| -------------------------------------------------------------- | ||||
| 
 | ||||
|  |  | |||
|  | @ -104,6 +104,22 @@ class TestModule(object): | |||
|             else: | ||||
|                 assert name not in stdout | ||||
| 
 | ||||
|     def test_show_traceback_import_error_unicode(self, testdir): | ||||
|         """Check test modules collected which raise ImportError with unicode messages | ||||
|         are handled properly (#2336). | ||||
|         """ | ||||
|         testdir.makepyfile(u""" | ||||
|             # -*- coding: utf-8 -*- | ||||
|             raise ImportError(u'Something bad happened ☺') | ||||
|         """) | ||||
|         result = testdir.runpytest() | ||||
|         result.stdout.fnmatch_lines([ | ||||
|             "ImportError while importing test module*", | ||||
|             "Traceback:", | ||||
|             "*raise ImportError*Something bad happened*", | ||||
|         ]) | ||||
|         assert result.ret == 2 | ||||
| 
 | ||||
| 
 | ||||
| class TestClass(object): | ||||
|     def test_class_with_init_warning(self, testdir): | ||||
|  | @ -826,6 +842,34 @@ class TestConftestCustomization(object): | |||
|         l = modcol.collect() | ||||
|         assert '_hello' not in l | ||||
| 
 | ||||
|     def test_issue2369_collect_module_fileext(self, testdir): | ||||
|         """Ensure we can collect files with weird file extensions as Python | ||||
|         modules (#2369)""" | ||||
|         # We'll implement a little finder and loader to import files containing | ||||
|         # Python source code whose file extension is ".narf". | ||||
|         testdir.makeconftest(""" | ||||
|             import sys, os, imp | ||||
|             from _pytest.python import Module | ||||
| 
 | ||||
|             class Loader: | ||||
|                 def load_module(self, name): | ||||
|                     return imp.load_source(name, name + ".narf") | ||||
|             class Finder: | ||||
|                 def find_module(self, name, path=None): | ||||
|                     if os.path.exists(name + ".narf"): | ||||
|                         return Loader() | ||||
|             sys.meta_path.append(Finder()) | ||||
| 
 | ||||
|             def pytest_collect_file(path, parent): | ||||
|                 if path.ext == ".narf": | ||||
|                     return Module(path, parent)""") | ||||
|         testdir.makefile(".narf", """ | ||||
|             def test_something(): | ||||
|                 assert 1 + 1 == 2""") | ||||
|         # Use runpytest_subprocess, since we're futzing with sys.meta_path. | ||||
|         result = testdir.runpytest_subprocess() | ||||
|         result.stdout.fnmatch_lines('*1 passed*') | ||||
| 
 | ||||
| def test_setup_only_available_in_subdir(testdir): | ||||
|     sub1 = testdir.mkpydir("sub1") | ||||
|     sub2 = testdir.mkpydir("sub2") | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ from __future__ import with_statement | |||
| import pickle | ||||
| import os | ||||
| import sys | ||||
| from io import UnsupportedOperation | ||||
| 
 | ||||
| import _pytest._code | ||||
| import py | ||||
|  | @ -671,7 +672,7 @@ def test_dontreadfrominput(): | |||
|     pytest.raises(IOError, f.read) | ||||
|     pytest.raises(IOError, f.readlines) | ||||
|     pytest.raises(IOError, iter, f) | ||||
|     pytest.raises(ValueError, f.fileno) | ||||
|     pytest.raises(UnsupportedOperation, f.fileno) | ||||
|     f.close()  # just for completeness | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -305,6 +305,23 @@ def test_parametrized_collected_from_command_line(testdir): | |||
|     rec.assertoutcome(passed=3) | ||||
| 
 | ||||
| 
 | ||||
| def test_parametrized_collect_with_wrong_args(testdir): | ||||
|     """Test collect parametrized func with wrong number of args.""" | ||||
|     py_file = testdir.makepyfile(""" | ||||
|         import pytest | ||||
| 
 | ||||
|         @pytest.mark.parametrize('foo, bar', [(1, 2, 3)]) | ||||
|         def test_func(foo, bar): | ||||
|             pass | ||||
|     """) | ||||
| 
 | ||||
|     result = testdir.runpytest(py_file) | ||||
|     result.stdout.fnmatch_lines([ | ||||
|         'E   ValueError: In "parametrize" the number of values ((1, 2, 3)) ' | ||||
|         'must be equal to the number of names ([\'foo\', \'bar\'])' | ||||
|     ]) | ||||
| 
 | ||||
| 
 | ||||
| class TestFunctional(object): | ||||
| 
 | ||||
|     def test_mark_per_function(self, testdir): | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue