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: | matrix: | ||||||
|   include: |   include: | ||||||
|     - env: TOXENV=py36 |     - env: TOXENV=py36 | ||||||
|       python: '3.6-dev' |       python: '3.6' | ||||||
|     - env: TOXENV=py37 |     - env: TOXENV=py37 | ||||||
|       python: 'nightly' |       python: 'nightly' | ||||||
|   allow_failures: |   allow_failures: | ||||||
|  |  | ||||||
							
								
								
									
										5
									
								
								AUTHORS
								
								
								
								
							
							
						
						
									
										5
									
								
								AUTHORS
								
								
								
								
							|  | @ -6,6 +6,7 @@ Contributors include:: | ||||||
| Abdeali JK | Abdeali JK | ||||||
| Abhijeet Kasurde | Abhijeet Kasurde | ||||||
| Ahn Ki-Wook | Ahn Ki-Wook | ||||||
|  | Alexander Johnson | ||||||
| Alexei Kozlenok | Alexei Kozlenok | ||||||
| Anatoly Bubenkoff | Anatoly Bubenkoff | ||||||
| Andreas Zeidler | Andreas Zeidler | ||||||
|  | @ -86,6 +87,7 @@ Justyna Janczyszyn | ||||||
| Kale Kundert | Kale Kundert | ||||||
| Katarzyna Jachim | Katarzyna Jachim | ||||||
| Kevin Cox | Kevin Cox | ||||||
|  | Kodi B. Arfer | ||||||
| Lee Kamentsky | Lee Kamentsky | ||||||
| Lev Maximov | Lev Maximov | ||||||
| Loic Esteve | Loic Esteve | ||||||
|  | @ -122,6 +124,7 @@ Oliver Bestwalter | ||||||
| Omar Kohl | Omar Kohl | ||||||
| Omer Hadari | Omer Hadari | ||||||
| Patrick Hayes | Patrick Hayes | ||||||
|  | Paweł Adamczak | ||||||
| Pieter Mulder | Pieter Mulder | ||||||
| Piotr Banaszkiewicz | Piotr Banaszkiewicz | ||||||
| Punyashloka Biswal | Punyashloka Biswal | ||||||
|  | @ -140,6 +143,7 @@ Russel Winder | ||||||
| Ryan Wooden | Ryan Wooden | ||||||
| Samuele Pedroni | Samuele Pedroni | ||||||
| Simon Gomizelj | Simon Gomizelj | ||||||
|  | Skylar Downes | ||||||
| Stefan Farmbauer | Stefan Farmbauer | ||||||
| Stefan Zimmermann | Stefan Zimmermann | ||||||
| Stefano Taschini | Stefano Taschini | ||||||
|  | @ -155,5 +159,6 @@ Vasily Kuznetsov | ||||||
| Victor Uriarte | Victor Uriarte | ||||||
| Vlad Dragos | Vlad Dragos | ||||||
| Vidar T. Fauske | Vidar T. Fauske | ||||||
|  | Vitaly Lashmanov | ||||||
| Wouter van Ackooy | Wouter van Ackooy | ||||||
| Xuecong Liao | Xuecong Liao | ||||||
|  |  | ||||||
|  | @ -121,7 +121,22 @@ Bug Fixes | ||||||
| 3.0.8 (unreleased) | 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) | 3.0.7 (2017-03-14) | ||||||
|  | @ -138,7 +166,7 @@ Bug Fixes | ||||||
| 
 | 
 | ||||||
| * Fix issue in assertion rewriting breaking due to modules silently discarding | * Fix issue in assertion rewriting breaking due to modules silently discarding | ||||||
|   other modules when importing fails |   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. |   Thanks `@pfhayes`_ for the PR. | ||||||
| 
 | 
 | ||||||
| * junitxml: Fix problematic case where system-out tag occured twice per testcase | * junitxml: Fix problematic case where system-out tag occured twice per testcase | ||||||
|  | @ -401,6 +429,7 @@ Bug Fixes | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| 3.0.2 (2016-09-01) | 3.0.2 (2016-09-01) | ||||||
| ================== | ================== | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										15
									
								
								README.rst
								
								
								
								
							
							
						
						
									
										15
									
								
								README.rst
								
								
								
								
							|  | @ -6,13 +6,20 @@ | ||||||
| ------ | ------ | ||||||
| 
 | 
 | ||||||
| .. image:: https://img.shields.io/pypi/v/pytest.svg | .. image:: https://img.shields.io/pypi/v/pytest.svg | ||||||
|    :target: https://pypi.python.org/pypi/pytest |     :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 | .. image:: https://img.shields.io/pypi/pyversions/pytest.svg | ||||||
|   :target: https://pypi.python.org/pypi/pytest |     :target: https://pypi.python.org/pypi/pytest | ||||||
|  | 
 | ||||||
| .. image:: https://img.shields.io/coveralls/pytest-dev/pytest/master.svg | .. image:: https://img.shields.io/coveralls/pytest-dev/pytest/master.svg | ||||||
|    :target: https://coveralls.io/r/pytest-dev/pytest |     :target: https://coveralls.io/r/pytest-dev/pytest | ||||||
|  | 
 | ||||||
| .. image:: https://travis-ci.org/pytest-dev/pytest.svg?branch=master | .. image:: https://travis-ci.org/pytest-dev/pytest.svg?branch=master | ||||||
|     :target: https://travis-ci.org/pytest-dev/pytest |     :target: https://travis-ci.org/pytest-dev/pytest | ||||||
|  | 
 | ||||||
| .. image:: https://ci.appveyor.com/api/projects/status/mrgbjaua7t33pg6b?svg=true | .. image:: https://ci.appveyor.com/api/projects/status/mrgbjaua7t33pg6b?svg=true | ||||||
|     :target: https://ci.appveyor.com/project/pytestbot/pytest |     :target: https://ci.appveyor.com/project/pytestbot/pytest | ||||||
| 
 | 
 | ||||||
|  | @ -34,7 +41,7 @@ An example of a simple test: | ||||||
| To execute it:: | To execute it:: | ||||||
| 
 | 
 | ||||||
|     $ pytest |     $ pytest | ||||||
|     ============================= test session starts =============================     |     ============================= test session starts ============================= | ||||||
|     collected 1 items |     collected 1 items | ||||||
| 
 | 
 | ||||||
|     test_sample.py F |     test_sample.py F | ||||||
|  |  | ||||||
|  | @ -3,13 +3,14 @@ import sys | ||||||
| from inspect import CO_VARARGS, CO_VARKEYWORDS | from inspect import CO_VARARGS, CO_VARKEYWORDS | ||||||
| import re | import re | ||||||
| from weakref import ref | from weakref import ref | ||||||
|  | from _pytest.compat import _PY2, _PY3, PY35 | ||||||
| 
 | 
 | ||||||
| import py | import py | ||||||
| builtin_repr = repr | builtin_repr = repr | ||||||
| 
 | 
 | ||||||
| reprlib = py.builtin._tryimport('repr', 'reprlib') | reprlib = py.builtin._tryimport('repr', 'reprlib') | ||||||
| 
 | 
 | ||||||
| if sys.version_info[0] >= 3: | if _PY3: | ||||||
|     from traceback import format_exception_only |     from traceback import format_exception_only | ||||||
| else: | else: | ||||||
|     from ._py2traceback import format_exception_only |     from ._py2traceback import format_exception_only | ||||||
|  | @ -353,7 +354,7 @@ class ExceptionInfo(object): | ||||||
|         help for navigating the traceback. |         help for navigating the traceback. | ||||||
|     """ |     """ | ||||||
|     _striptext = '' |     _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): |     def __init__(self, tup=None, exprinfo=None): | ||||||
|         import _pytest._code |         import _pytest._code | ||||||
|  | @ -618,7 +619,7 @@ class FormattedExcinfo(object): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     def repr_excinfo(self, excinfo): |     def repr_excinfo(self, excinfo): | ||||||
|         if sys.version_info[0] < 3: |         if _PY2: | ||||||
|             reprtraceback = self.repr_traceback(excinfo) |             reprtraceback = self.repr_traceback(excinfo) | ||||||
|             reprcrash = excinfo._getreprcrash() |             reprcrash = excinfo._getreprcrash() | ||||||
| 
 | 
 | ||||||
|  | @ -655,7 +656,7 @@ class FormattedExcinfo(object): | ||||||
| class TerminalRepr(object): | class TerminalRepr(object): | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|         s = self.__unicode__() |         s = self.__unicode__() | ||||||
|         if sys.version_info[0] < 3: |         if _PY2: | ||||||
|             s = s.encode('utf-8') |             s = s.encode('utf-8') | ||||||
|         return s |         return s | ||||||
| 
 | 
 | ||||||
|  | @ -851,7 +852,7 @@ def getrawcode(obj, trycall=True): | ||||||
|         return obj |         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): |     def is_recursion_error(excinfo): | ||||||
|         return excinfo.errisinstance(RecursionError)  # noqa |         return excinfo.errisinstance(RecursionError)  # noqa | ||||||
| else: | else: | ||||||
|  |  | ||||||
|  | @ -28,6 +28,7 @@ _PY2 = not _PY3 | ||||||
| NoneType = type(None) | NoneType = type(None) | ||||||
| NOTSET = object() | NOTSET = object() | ||||||
| 
 | 
 | ||||||
|  | PY35 = sys.version_info[:2] >= (3, 5) | ||||||
| PY36 = sys.version_info[:2] >= (3, 6) | PY36 = sys.version_info[:2] >= (3, 6) | ||||||
| MODULE_NOT_FOUND_ERROR = 'ModuleNotFoundError' if PY36 else 'ImportError' | MODULE_NOT_FOUND_ERROR = 'ModuleNotFoundError' if PY36 else 'ImportError' | ||||||
| 
 | 
 | ||||||
|  | @ -250,8 +251,10 @@ else: | ||||||
|         try: |         try: | ||||||
|             return str(v) |             return str(v) | ||||||
|         except UnicodeError: |         except UnicodeError: | ||||||
|  |             if not isinstance(v, unicode): | ||||||
|  |                 v = unicode(v) | ||||||
|             errors = 'replace' |             errors = 'replace' | ||||||
|             return v.encode('ascii', errors) |             return v.encode('utf-8', errors) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| COLLECT_FAKEMODULE_ATTRIBUTES = ( | COLLECT_FAKEMODULE_ATTRIBUTES = ( | ||||||
|  |  | ||||||
|  | @ -826,8 +826,8 @@ class FixtureFunctionMarker(object): | ||||||
| def fixture(scope="function", params=None, autouse=False, ids=None, name=None): | def fixture(scope="function", params=None, autouse=False, ids=None, name=None): | ||||||
|     """ (return a) decorator to mark a fixture factory function. |     """ (return a) decorator to mark a fixture factory function. | ||||||
| 
 | 
 | ||||||
|     This decorator can be used (with or or without parameters) to define |     This decorator can be used (with or without parameters) to define a | ||||||
|     a fixture function.  The name of the fixture function can later be |     fixture function.  The name of the fixture function can later be | ||||||
|     referenced to cause its invocation ahead of running tests: test |     referenced to cause its invocation ahead of running tests: test | ||||||
|     modules or classes can use the pytest.mark.usefixtures(fixturename) |     modules or classes can use the pytest.mark.usefixtures(fixturename) | ||||||
|     marker.  Test functions can directly use fixture names as input |     marker.  Test functions can directly use fixture names as input | ||||||
|  | @ -846,16 +846,16 @@ def fixture(scope="function", params=None, autouse=False, ids=None, name=None): | ||||||
|                 reference is needed to activate the fixture. |                 reference is needed to activate the fixture. | ||||||
| 
 | 
 | ||||||
|     :arg ids: list of string ids each corresponding to the params |     :arg ids: list of string ids each corresponding to the params | ||||||
|        so that they are part of the test id. If no ids are provided |                 so that they are part of the test id. If no ids are provided | ||||||
|        they will be generated automatically from the params. |                 they will be generated automatically from the params. | ||||||
| 
 | 
 | ||||||
|     :arg name: the name of the fixture. This defaults to the name of the |     :arg name: the name of the fixture. This defaults to the name of the | ||||||
|                decorated function. If a fixture is used in the same module in |                 decorated function. If a fixture is used in the same module in | ||||||
|                which it is defined, the function name of the fixture will be |                 which it is defined, the function name of the fixture will be | ||||||
|                shadowed by the function arg that requests the fixture; one way |                 shadowed by the function arg that requests the fixture; one way | ||||||
|                to resolve this is to name the decorated function |                 to resolve this is to name the decorated function | ||||||
|                ``fixture_<fixturename>`` and then use |                 ``fixture_<fixturename>`` and then use | ||||||
|                ``@pytest.fixture(name='<fixturename>')``. |                 ``@pytest.fixture(name='<fixturename>')``. | ||||||
| 
 | 
 | ||||||
|     Fixtures can optionally provide their values to test functions using a ``yield`` statement, |     Fixtures can optionally provide their values to test functions using a ``yield`` statement, | ||||||
|     instead of ``return``. In this case, the code block after the ``yield`` statement is executed |     instead of ``return``. In this case, the code block after the ``yield`` statement is executed | ||||||
|  |  | ||||||
|  | @ -452,9 +452,10 @@ class Testdir(object): | ||||||
|         the module is re-imported. |         the module is re-imported. | ||||||
|         """ |         """ | ||||||
|         for name in set(sys.modules).difference(self._savemodulekeys): |         for name in set(sys.modules).difference(self._savemodulekeys): | ||||||
|             # zope.interface (used by twisted-related tests) keeps internal |             # some zope modules used by twisted-related tests keeps internal | ||||||
|             # state and can't be deleted |             # state and can't be deleted; we had some trouble in the past | ||||||
|             if not name.startswith("zope.interface"): |             # with zope.interface for example | ||||||
|  |             if not name.startswith("zope"): | ||||||
|                 del sys.modules[name] |                 del sys.modules[name] | ||||||
| 
 | 
 | ||||||
|     def make_hook_recorder(self, pluginmanager): |     def make_hook_recorder(self, pluginmanager): | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ from __future__ import absolute_import, division, print_function | ||||||
| import fnmatch | import fnmatch | ||||||
| import inspect | import inspect | ||||||
| import sys | import sys | ||||||
|  | import os | ||||||
| import collections | import collections | ||||||
| import math | import math | ||||||
| from itertools import count | from itertools import count | ||||||
|  | @ -20,7 +21,7 @@ from _pytest.compat import ( | ||||||
|     isclass, isfunction, is_generator, _escape_strings, |     isclass, isfunction, is_generator, _escape_strings, | ||||||
|     REGEX_TYPE, STRING_TYPES, NoneType, NOTSET, |     REGEX_TYPE, STRING_TYPES, NoneType, NOTSET, | ||||||
|     get_real_func, getfslineno, safe_getattr, |     get_real_func, getfslineno, safe_getattr, | ||||||
|     getlocation, enum, |     safe_str, getlocation, enum, | ||||||
| ) | ) | ||||||
| from _pytest.runner import fail | from _pytest.runner import fail | ||||||
| 
 | 
 | ||||||
|  | @ -223,8 +224,7 @@ class PyobjMixin(PyobjContext): | ||||||
|                 continue |                 continue | ||||||
|             name = node.name |             name = node.name | ||||||
|             if isinstance(node, Module): |             if isinstance(node, Module): | ||||||
|                 assert name.endswith(".py") |                 name = os.path.splitext(name)[0] | ||||||
|                 name = name[:-3] |  | ||||||
|                 if stopatmodule: |                 if stopatmodule: | ||||||
|                     if includemodule: |                     if includemodule: | ||||||
|                         parts.append(name) |                         parts.append(name) | ||||||
|  | @ -427,7 +427,7 @@ class Module(main.File, PyCollector): | ||||||
|             if self.config.getoption('verbose') < 2: |             if self.config.getoption('verbose') < 2: | ||||||
|                 exc_info.traceback = exc_info.traceback.filter(filter_traceback) |                 exc_info.traceback = exc_info.traceback.filter(filter_traceback) | ||||||
|             exc_repr = exc_info.getrepr(style='short') if exc_info.traceback else exc_info.exconly() |             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( |             raise self.CollectError( | ||||||
|                 "ImportError while importing test module '{fspath}'.\n" |                 "ImportError while importing test module '{fspath}'.\n" | ||||||
|                 "Hint: make sure your test modules/packages have valid Python names.\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)]: |         for callspec in self._calls or [CallSpec2(self)]: | ||||||
|             elements = zip(ids, parameters, count()) |             elements = zip(ids, parameters, count()) | ||||||
|             for a_id, param, param_index in elements: |             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 = callspec.copy(self) | ||||||
|                 newcallspec.setmulti(valtypes, argnames, param.values, a_id, |                 newcallspec.setmulti(valtypes, argnames, param.values, a_id, | ||||||
|                                      param.deprecated_arg_dict, scopenum, param_index) |                                      param.deprecated_arg_dict, scopenum, param_index) | ||||||
|  |  | ||||||
|  | @ -270,12 +270,21 @@ supporting modules which are not themselves test modules will not be rewritten. | ||||||
| 
 | 
 | ||||||
| .. note:: | .. note:: | ||||||
| 
 | 
 | ||||||
|    ``pytest`` rewrites test modules on import. It does this by using an import |    ``pytest`` rewrites test modules on import by using an import | ||||||
|    hook to write new pyc files. Most of the time this works transparently. |    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 |    However, if you are messing with import yourself, the import hook may | ||||||
|    interfere. If this is the case, use ``--assert=plain``. Additionally, |    interfere. | ||||||
|    rewriting will fail silently if it cannot write new pycs, i.e. in a read-only | 
 | ||||||
|    filesystem or a zipfile. |    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>`_. | 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 |    getting-started | ||||||
|    usage |    usage | ||||||
|  |    existingtestsuite | ||||||
|    assert |    assert | ||||||
|    builtin |    builtin | ||||||
|    fixture |    fixture | ||||||
|  |  | ||||||
|  | @ -45,11 +45,11 @@ Here is the algorithm which finds the rootdir from ``args``: | ||||||
|   matched, it becomes the ini-file and its directory becomes the rootdir. |   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 | - 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. |   a package and don't have any particular ini-file configuration. | ||||||
| 
 | 
 | ||||||
| If no ``args`` are given, pytest collects test below the current working | If no ``args`` are given, pytest collects test below the current working | ||||||
| directory and also starts determining the rootdir from there.  | directory and also starts determining the rootdir from there. | ||||||
| 
 | 
 | ||||||
| :warning: custom pytest plugin commandline arguments may include a path, as in | :warning: custom pytest plugin commandline arguments may include a path, as in | ||||||
|     ``pytest --log-output ../../test.log args``. Then ``args`` is mandatory, |     ``pytest --log-output ../../test.log args``. Then ``args`` is mandatory, | ||||||
|  | @ -97,6 +97,8 @@ check for ini-files as follows:: | ||||||
| .. _`how to change command line options defaults`: | .. _`how to change command line options defaults`: | ||||||
| .. _`adding default options`: | .. _`adding default options`: | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| How to change command line options defaults | 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 |     import pytest | ||||||
| 
 | 
 | ||||||
|     @pytest.fixture(scope="module") |     @pytest.fixture(scope="module") | ||||||
|     def smtp(request): |     def smtp(): | ||||||
|         smtp = smtplib.SMTP("smtp.gmail.com") |         smtp = smtplib.SMTP("smtp.gmail.com") | ||||||
|         yield smtp  # provide the fixture value |         yield smtp  # provide the fixture value | ||||||
|         print("teardown smtp") |         print("teardown smtp") | ||||||
|  | @ -287,7 +287,7 @@ Note that we can also seamlessly use the ``yield`` syntax with ``with`` statemen | ||||||
|     import pytest |     import pytest | ||||||
| 
 | 
 | ||||||
|     @pytest.fixture(scope="module") |     @pytest.fixture(scope="module") | ||||||
|     def smtp(request): |     def smtp(): | ||||||
|         with smtplib.SMTP("smtp.gmail.com") as smtp: |         with smtplib.SMTP("smtp.gmail.com") as smtp: | ||||||
|             yield smtp  # provide the fixture value |             yield smtp  # provide the fixture value | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -49,17 +49,17 @@ That's it. You can execute the test function now:: | ||||||
|     platform linux -- Python 3.5.2, pytest-3.0.7, py-1.4.32, pluggy-0.4.0 |     platform linux -- Python 3.5.2, pytest-3.0.7, py-1.4.32, pluggy-0.4.0 | ||||||
|     rootdir: $REGENDOC_TMPDIR, inifile: |     rootdir: $REGENDOC_TMPDIR, inifile: | ||||||
|     collected 1 items |     collected 1 items | ||||||
|      | 
 | ||||||
|     test_sample.py F |     test_sample.py F | ||||||
|      | 
 | ||||||
|     ======= FAILURES ======== |     ======= FAILURES ======== | ||||||
|     _______ test_answer ________ |     _______ test_answer ________ | ||||||
|      | 
 | ||||||
|         def test_answer(): |         def test_answer(): | ||||||
|     >       assert func(3) == 5 |     >       assert func(3) == 5 | ||||||
|     E       assert 4 == 5 |     E       assert 4 == 5 | ||||||
|     E        +  where 4 = func(3) |     E        +  where 4 = func(3) | ||||||
|      | 
 | ||||||
|     test_sample.py:5: AssertionError |     test_sample.py:5: AssertionError | ||||||
|     ======= 1 failed in 0.12 seconds ======== |     ======= 1 failed in 0.12 seconds ======== | ||||||
| 
 | 
 | ||||||
|  | @ -128,15 +128,15 @@ run the module by passing its filename:: | ||||||
|     .F |     .F | ||||||
|     ======= FAILURES ======== |     ======= FAILURES ======== | ||||||
|     _______ TestClass.test_two ________ |     _______ TestClass.test_two ________ | ||||||
|      | 
 | ||||||
|     self = <test_class.TestClass object at 0xdeadbeef> |     self = <test_class.TestClass object at 0xdeadbeef> | ||||||
|      | 
 | ||||||
|         def test_two(self): |         def test_two(self): | ||||||
|             x = "hello" |             x = "hello" | ||||||
|     >       assert hasattr(x, 'check') |     >       assert hasattr(x, 'check') | ||||||
|     E       AssertionError: assert False |     E       AssertionError: assert False | ||||||
|     E        +  where False = hasattr('hello', 'check') |     E        +  where False = hasattr('hello', 'check') | ||||||
|      | 
 | ||||||
|     test_class.py:8: AssertionError |     test_class.py:8: AssertionError | ||||||
|     1 failed, 1 passed in 0.12 seconds |     1 failed, 1 passed in 0.12 seconds | ||||||
| 
 | 
 | ||||||
|  | @ -165,14 +165,14 @@ before performing the test function call.  Let's just run it:: | ||||||
|     F |     F | ||||||
|     ======= FAILURES ======== |     ======= FAILURES ======== | ||||||
|     _______ test_needsfiles ________ |     _______ test_needsfiles ________ | ||||||
|      | 
 | ||||||
|     tmpdir = local('PYTEST_TMPDIR/test_needsfiles0') |     tmpdir = local('PYTEST_TMPDIR/test_needsfiles0') | ||||||
|      | 
 | ||||||
|         def test_needsfiles(tmpdir): |         def test_needsfiles(tmpdir): | ||||||
|             print (tmpdir) |             print (tmpdir) | ||||||
|     >       assert 0 |     >       assert 0 | ||||||
|     E       assert 0 |     E       assert 0 | ||||||
|      | 
 | ||||||
|     test_tmpdir.py:3: AssertionError |     test_tmpdir.py:3: AssertionError | ||||||
|     --------------------------- Captured stdout call --------------------------- |     --------------------------- Captured stdout call --------------------------- | ||||||
|     PYTEST_TMPDIR/test_needsfiles0 |     PYTEST_TMPDIR/test_needsfiles0 | ||||||
|  | @ -192,6 +192,7 @@ Here are a few suggestions where to go next: | ||||||
| 
 | 
 | ||||||
| * :ref:`cmdline` for command line invocation examples | * :ref:`cmdline` for command line invocation examples | ||||||
| * :ref:`good practices <goodpractices>` for virtualenv, test layout | * :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:`fixtures` for providing a functional baseline to your tests | ||||||
| * :ref:`plugins` managing and writing plugins | * :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 | Choosing a test layout / import rules | ||||||
| ------------------------------------------ | ------------------------------------- | ||||||
| 
 | 
 | ||||||
| ``pytest`` supports two common test layouts: | ``pytest`` supports two common test layouts: | ||||||
| 
 | 
 | ||||||
| * putting tests into an extra directory outside your actual application | Tests outside application code | ||||||
|   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):: |  | ||||||
| 
 | 
 | ||||||
|     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/ |     mypkg/ | ||||||
|         __init__.py |         __init__.py | ||||||
|         appmodule.py |         app.py | ||||||
|  |         view.py | ||||||
|     tests/ |     tests/ | ||||||
|         test_app.py |         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 | Note that using this scheme your test files must have **unique names**, because | ||||||
|   have direct relation between (unit-)test and application modules and | ``pytest`` will import them as *top-level* modules since there are no packages | ||||||
|   want to distribute your tests along with your application:: | 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 | ||||||
|  |             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/ |     mypkg/ | ||||||
|         __init__.py |         __init__.py | ||||||
|         appmodule.py |         app.py | ||||||
|         ... |         view.py | ||||||
|         test/ |         test/ | ||||||
|  |             __init__.py | ||||||
|             test_app.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**. | Note that this layout also works in conjunction with the ``src`` layout mentioned in the previous section. | ||||||
|   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. |  | ||||||
| 
 | 
 | ||||||
| - 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:: | .. note:: | ||||||
| 
 | 
 | ||||||
|  | @ -144,7 +182,15 @@ for installing your application and any dependencies | ||||||
| as well as the ``pytest`` package itself. This ensures your code and | as well as the ``pytest`` package itself. This ensures your code and | ||||||
| dependencies are isolated from the system Python installation. | 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 | package passes all tests you may want to look into `tox`_, the | ||||||
| virtualenv test automation tool and its `pytest support | virtualenv test automation tool and its `pytest support | ||||||
| <https://tox.readthedocs.io/en/latest/example/pytest.html>`_. | <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 | against your source code checkout, helping to detect packaging | ||||||
| glitches. | 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`` | 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 |   ``tests.test_mod``) but different file system paths | ||||||
|   (e.g. ``tests/test_mode.py`` and ``other/tests/test_mode.py``) |   (e.g. ``tests/test_mode.py`` and ``other/tests/test_mode.py``) | ||||||
|   by extending sys.path/import semantics.   pytest does not do that |   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>`_. |   `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, | - nose-style doctests are not collected and executed correctly, | ||||||
|   also doctest fixtures don't work. |   also doctest fixtures don't work. | ||||||
| 
 | 
 | ||||||
|  | @ -62,3 +72,4 @@ Unsupported idioms / known issues | ||||||
|   being the recommended alternative. |   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 [...]`` | 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 | ||||||
|  | -------------------------------------------------------------- | ||||||
|  | 
 | ||||||
|  | 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 | Getting help on version, option names, environment variables | ||||||
| -------------------------------------------------------------- | -------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -104,6 +104,22 @@ class TestModule(object): | ||||||
|             else: |             else: | ||||||
|                 assert name not in stdout |                 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): | class TestClass(object): | ||||||
|     def test_class_with_init_warning(self, testdir): |     def test_class_with_init_warning(self, testdir): | ||||||
|  | @ -826,6 +842,34 @@ class TestConftestCustomization(object): | ||||||
|         l = modcol.collect() |         l = modcol.collect() | ||||||
|         assert '_hello' not in l |         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): | def test_setup_only_available_in_subdir(testdir): | ||||||
|     sub1 = testdir.mkpydir("sub1") |     sub1 = testdir.mkpydir("sub1") | ||||||
|     sub2 = testdir.mkpydir("sub2") |     sub2 = testdir.mkpydir("sub2") | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ from __future__ import with_statement | ||||||
| import pickle | import pickle | ||||||
| import os | import os | ||||||
| import sys | import sys | ||||||
|  | from io import UnsupportedOperation | ||||||
| 
 | 
 | ||||||
| import _pytest._code | import _pytest._code | ||||||
| import py | import py | ||||||
|  | @ -671,7 +672,7 @@ def test_dontreadfrominput(): | ||||||
|     pytest.raises(IOError, f.read) |     pytest.raises(IOError, f.read) | ||||||
|     pytest.raises(IOError, f.readlines) |     pytest.raises(IOError, f.readlines) | ||||||
|     pytest.raises(IOError, iter, f) |     pytest.raises(IOError, iter, f) | ||||||
|     pytest.raises(ValueError, f.fileno) |     pytest.raises(UnsupportedOperation, f.fileno) | ||||||
|     f.close()  # just for completeness |     f.close()  # just for completeness | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -305,6 +305,23 @@ def test_parametrized_collected_from_command_line(testdir): | ||||||
|     rec.assertoutcome(passed=3) |     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): | class TestFunctional(object): | ||||||
| 
 | 
 | ||||||
|     def test_mark_per_function(self, testdir): |     def test_mark_per_function(self, testdir): | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue