Merge branch 'master' into features
This commit is contained in:
		
						commit
						6c170201d6
					
				
							
								
								
									
										1
									
								
								AUTHORS
								
								
								
								
							
							
						
						
									
										1
									
								
								AUTHORS
								
								
								
								
							|  | @ -33,6 +33,7 @@ Endre Galaczi | ||||||
| Elizaveta Shashkova | Elizaveta Shashkova | ||||||
| Eric Hunsberger | Eric Hunsberger | ||||||
| Eric Siegerman | Eric Siegerman | ||||||
|  | Erik M. Bray | ||||||
| Florian Bruhin | Florian Bruhin | ||||||
| Floris Bruynooghe | Floris Bruynooghe | ||||||
| Gabriel Reis | Gabriel Reis | ||||||
|  |  | ||||||
							
								
								
									
										14
									
								
								CHANGELOG
								
								
								
								
							
							
						
						
									
										14
									
								
								CHANGELOG
								
								
								
								
							|  | @ -40,6 +40,20 @@ Bug Fixes | ||||||
| 2.8.6.dev1 | 2.8.6.dev1 | ||||||
| ========== | ========== | ||||||
| 
 | 
 | ||||||
|  | - fix #1259: allow for double nodeids in junitxml, | ||||||
|  |   this was a regression failing plugins combinations | ||||||
|  |   like pytest-pep8 + pytest-flakes | ||||||
|  | 
 | ||||||
|  | - Workaround for exception that occurs in pyreadline when using | ||||||
|  |   ``--pdb`` with standard I/O capture enabled. | ||||||
|  |   Thanks Erik M. Bray for the PR. | ||||||
|  | 
 | ||||||
|  | - fix #900: Better error message in case the target of a ``monkeypatch`` call | ||||||
|  |   raises an ``ImportError``. | ||||||
|  | 
 | ||||||
|  | - fix #1292: monkeypatch calls (setattr, setenv, etc.) are now O(1). | ||||||
|  |   Thanks David R. MacIver for the report and Bruno Oliveira for the PR. | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| 2.8.5 | 2.8.5 | ||||||
| ===== | ===== | ||||||
|  |  | ||||||
							
								
								
									
										117
									
								
								README.rst
								
								
								
								
							
							
						
						
									
										117
									
								
								README.rst
								
								
								
								
							|  | @ -1,12 +1,14 @@ | ||||||
| ====== | .. image:: doc/en/img/pytest1.png | ||||||
| pytest |    :target: http://pytest.org | ||||||
| ====== |    :align: center | ||||||
|  |    :alt: pytest | ||||||
| 
 | 
 | ||||||
| The ``pytest`` testing tool makes it easy to write small tests, yet | ------ | ||||||
| scales to support complex functional testing. |  | ||||||
| 
 | 
 | ||||||
| .. 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://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 | .. 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 | ||||||
|  | @ -14,53 +16,84 @@ scales to support complex functional testing. | ||||||
| .. 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 | ||||||
| 
 | 
 | ||||||
| Documentation: http://pytest.org/latest/ | The ``pytest`` framework makes it easy to write small tests, yet | ||||||
|  | scales to support complex functional testing for applications and libraries.     | ||||||
| 
 | 
 | ||||||
| Changelog: http://pytest.org/latest/changelog.html | An example of a simple test: | ||||||
|  | 
 | ||||||
|  | .. code-block:: python | ||||||
|  | 
 | ||||||
|  |     # content of test_sample.py | ||||||
|  |     def func(x): | ||||||
|  |         return x + 1 | ||||||
|  | 
 | ||||||
|  |     def test_answer(): | ||||||
|  |         assert func(3) == 5 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | To execute it:: | ||||||
|  | 
 | ||||||
|  |     $ py.test | ||||||
|  |     ======= test session starts ======== | ||||||
|  |     platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1     | ||||||
|  |     collected 1 items | ||||||
|  | 
 | ||||||
|  |     test_sample.py F | ||||||
|  | 
 | ||||||
|  |     ======= FAILURES ======== | ||||||
|  |     _______ test_answer ________ | ||||||
|  | 
 | ||||||
|  |         def test_answer(): | ||||||
|  |     >       assert func(3) == 5 | ||||||
|  |     E       assert 4 == 5 | ||||||
|  |     E        +  where 4 = func(3) | ||||||
|  | 
 | ||||||
|  |     test_sample.py:5: AssertionError | ||||||
|  |     ======= 1 failed in 0.12 seconds ======== | ||||||
|  | 
 | ||||||
|  | Due to ``py.test``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started <http://pytest.org/latest/getting-started.html#our-first-test-run>`_ for more examples. | ||||||
|          |          | ||||||
| Issues: https://github.com/pytest-dev/pytest/issues |  | ||||||
| 
 | 
 | ||||||
| Features | Features | ||||||
| -------- | -------- | ||||||
| 
 | 
 | ||||||
| - `auto-discovery | - Detailed info on failing `assert statements <http://pytest.org/latest/assert.html>`_ (no need to remember ``self.assert*`` names); | ||||||
|  | 
 | ||||||
|  | - `Auto-discovery | ||||||
|   <http://pytest.org/latest/goodpractises.html#python-test-discovery>`_ |   <http://pytest.org/latest/goodpractises.html#python-test-discovery>`_ | ||||||
|   of test modules and functions, |   of test modules and functions; | ||||||
| - detailed info on failing `assert statements <http://pytest.org/latest/assert.html>`_ (no need to remember ``self.assert*`` names) | 
 | ||||||
| - `modular fixtures <http://pytest.org/latest/fixture.html>`_  for | - `Modular fixtures <http://pytest.org/latest/fixture.html>`_  for | ||||||
|   managing small or parametrized long-lived test resources. |   managing small or parametrized long-lived test resources; | ||||||
| - multi-paradigm support: you can use ``pytest`` to run test suites based | 
 | ||||||
|   on `unittest <http://pytest.org/latest/unittest.html>`_ (or trial), | - Can run `unittest <http://pytest.org/latest/unittest.html>`_ (or trial), | ||||||
|   `nose <http://pytest.org/latest/nose.html>`_ |   `nose <http://pytest.org/latest/nose.html>`_ test suites out of the box; | ||||||
| - single-source compatibility from Python2.6 all the way up to | 
 | ||||||
|   Python3.5, PyPy-2.3, (jython-2.5 untested) | - Python2.6+, Python3.2+, PyPy-2.3, Jython-2.5 (untested); | ||||||
|  | 
 | ||||||
|  | - Rich plugin architecture, with over 150+ `external plugins <http://pytest.org/latest/plugins.html#installing-external-plugins-searching>`_ and thriving comminity; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| - many `external plugins <http://pytest.org/latest/plugins.html#installing-external-plugins-searching>`_. | Documentation | ||||||
|  | ------------- | ||||||
| 
 | 
 | ||||||
| A simple example for a test: | For full documentation, including installation, tutorials and PDF documents, please see http://pytest.org. | ||||||
| 
 |  | ||||||
| .. code-block:: python |  | ||||||
| 
 |  | ||||||
|     # content of test_module.py |  | ||||||
|     def test_function(): |  | ||||||
|         i = 4 |  | ||||||
|         assert i == 3 |  | ||||||
| 
 |  | ||||||
| which can be run with ``py.test test_module.py``.  See `getting-started <http://pytest.org/latest/getting-started.html#our-first-test-run>`_ for more examples. |  | ||||||
| 
 |  | ||||||
| For much more info, including PDF docs, see |  | ||||||
| 
 |  | ||||||
|     http://pytest.org |  | ||||||
| 
 |  | ||||||
| and report bugs at: |  | ||||||
| 
 |  | ||||||
|     https://github.com/pytest-dev/pytest/issues |  | ||||||
| 
 |  | ||||||
| and checkout or fork repo at: |  | ||||||
| 
 |  | ||||||
|     https://github.com/pytest-dev/pytest |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Copyright Holger Krekel and others, 2004-2015 | Bugs/Requests | ||||||
|  | ------------- | ||||||
|  | 
 | ||||||
|  | Please use the `GitHub issue tracker <https://github.com/pytest-dev/pytest/issues>`_ to submit bugs or request features. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | Changelog | ||||||
|  | --------- | ||||||
|  | 
 | ||||||
|  | Consult the `Changelog <http://pytest.org/latest/changelog.html>`_ page for fixes and enhancements of each version. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | License | ||||||
|  | ------- | ||||||
|  | 
 | ||||||
|  | Copyright Holger Krekel and others, 2004-2016. | ||||||
| Licensed under the MIT license. | Licensed under the MIT license. | ||||||
|  |  | ||||||
|  | @ -31,6 +31,7 @@ def pytest_addoption(parser): | ||||||
| 
 | 
 | ||||||
| @pytest.hookimpl(hookwrapper=True) | @pytest.hookimpl(hookwrapper=True) | ||||||
| def pytest_load_initial_conftests(early_config, parser, args): | def pytest_load_initial_conftests(early_config, parser, args): | ||||||
|  |     _readline_workaround() | ||||||
|     ns = early_config.known_args_namespace |     ns = early_config.known_args_namespace | ||||||
|     pluginmanager = early_config.pluginmanager |     pluginmanager = early_config.pluginmanager | ||||||
|     capman = CaptureManager(ns.capture) |     capman = CaptureManager(ns.capture) | ||||||
|  | @ -442,3 +443,30 @@ class DontReadFromInput: | ||||||
| 
 | 
 | ||||||
|     def close(self): |     def close(self): | ||||||
|         pass |         pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def _readline_workaround(): | ||||||
|  |     """ | ||||||
|  |     Ensure readline is imported so that it attaches to the correct stdio | ||||||
|  |     handles on Windows. | ||||||
|  | 
 | ||||||
|  |     Pdb uses readline support where available--when not running from the Python | ||||||
|  |     prompt, the readline module is not imported until running the pdb REPL.  If | ||||||
|  |     running py.test with the --pdb option this means the readline module is not | ||||||
|  |     imported until after I/O capture has been started. | ||||||
|  | 
 | ||||||
|  |     This is a problem for pyreadline, which is often used to implement readline | ||||||
|  |     support on Windows, as it does not attach to the correct handles for stdout | ||||||
|  |     and/or stdin if they have been redirected by the FDCapture mechanism.  This | ||||||
|  |     workaround ensures that readline is imported before I/O capture is setup so | ||||||
|  |     that it can attach to the actual stdin/out for the console. | ||||||
|  | 
 | ||||||
|  |     See https://github.com/pytest-dev/pytest/pull/1281 | ||||||
|  |     """ | ||||||
|  | 
 | ||||||
|  |     if not sys.platform.startswith('win32'): | ||||||
|  |         return | ||||||
|  |     try: | ||||||
|  |         import readline  # noqa | ||||||
|  |     except ImportError: | ||||||
|  |         pass | ||||||
|  |  | ||||||
|  | @ -282,12 +282,11 @@ def pytest_keyboard_interrupt(excinfo): | ||||||
|     """ called for keyboard interrupt. """ |     """ called for keyboard interrupt. """ | ||||||
| 
 | 
 | ||||||
| def pytest_exception_interact(node, call, report): | def pytest_exception_interact(node, call, report): | ||||||
|     """ (experimental, new in 2.4) called when |     """called when an exception was raised which can potentially be | ||||||
|     an exception was raised which can potentially be |  | ||||||
|     interactively handled. |     interactively handled. | ||||||
| 
 | 
 | ||||||
|     This hook is only called if an exception was raised |     This hook is only called if an exception was raised | ||||||
|     that is not an internal exception like "skip.Exception". |     that is not an internal exception like ``skip.Exception``. | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
| def pytest_enter_pdb(config): | def pytest_enter_pdb(config): | ||||||
|  |  | ||||||
|  | @ -71,7 +71,6 @@ class _NodeReporter(object): | ||||||
|         self.testcase = None |         self.testcase = None | ||||||
|         self.attrs = {} |         self.attrs = {} | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def append(self, node): |     def append(self, node): | ||||||
|         self.xml.add_stats(type(node).__name__) |         self.xml.add_stats(type(node).__name__) | ||||||
|         self.nodes.append(node) |         self.nodes.append(node) | ||||||
|  | @ -82,7 +81,6 @@ class _NodeReporter(object): | ||||||
|             self.property_insert_order.append(name) |             self.property_insert_order.append(name) | ||||||
|         self.properties[name] = bin_xml_escape(value) |         self.properties[name] = bin_xml_escape(value) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def make_properties_node(self): |     def make_properties_node(self): | ||||||
|         """Return a Junit node containing custom properties, if any. |         """Return a Junit node containing custom properties, if any. | ||||||
|         """ |         """ | ||||||
|  | @ -93,7 +91,6 @@ class _NodeReporter(object): | ||||||
|             ]) |             ]) | ||||||
|         return '' |         return '' | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def record_testreport(self, testreport): |     def record_testreport(self, testreport): | ||||||
|         assert not self.testcase |         assert not self.testcase | ||||||
|         names = mangle_testnames(testreport.nodeid.split("::")) |         names = mangle_testnames(testreport.nodeid.split("::")) | ||||||
|  | @ -182,7 +179,6 @@ class _NodeReporter(object): | ||||||
|                               message=skipreason)) |                               message=skipreason)) | ||||||
|         self._write_captured_output(report) |         self._write_captured_output(report) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def finalize(self): |     def finalize(self): | ||||||
|         data = self.to_xml().unicode(indent=0) |         data = self.to_xml().unicode(indent=0) | ||||||
|         self.__dict__.clear() |         self.__dict__.clear() | ||||||
|  | @ -262,6 +258,14 @@ class LogXML(object): | ||||||
|         self.node_reporters = {}  # nodeid -> _NodeReporter |         self.node_reporters = {}  # nodeid -> _NodeReporter | ||||||
|         self.node_reporters_ordered = [] |         self.node_reporters_ordered = [] | ||||||
| 
 | 
 | ||||||
|  |     def finalize(self, report): | ||||||
|  |         nodeid = getattr(report, 'nodeid', report) | ||||||
|  |         # local hack to handle xdist report order | ||||||
|  |         slavenode = getattr(report, 'node', None) | ||||||
|  |         reporter = self.node_reporters.pop((nodeid, slavenode)) | ||||||
|  |         if reporter is not None: | ||||||
|  |             reporter.finalize() | ||||||
|  | 
 | ||||||
|     def node_reporter(self, report): |     def node_reporter(self, report): | ||||||
|         nodeid = getattr(report, 'nodeid', report) |         nodeid = getattr(report, 'nodeid', report) | ||||||
|         # local hack to handle xdist report order |         # local hack to handle xdist report order | ||||||
|  | @ -324,7 +328,7 @@ class LogXML(object): | ||||||
|             reporter.append_skipped(report) |             reporter.append_skipped(report) | ||||||
|         self.update_testcase_duration(report) |         self.update_testcase_duration(report) | ||||||
|         if report.when == "teardown": |         if report.when == "teardown": | ||||||
|             self.node_reporter(report).finalize() |             self.finalize(report) | ||||||
| 
 | 
 | ||||||
|     def update_testcase_duration(self, report): |     def update_testcase_duration(self, report): | ||||||
|         """accumulates total duration for nodeid from given report and updates |         """accumulates total duration for nodeid from given report and updates | ||||||
|  |  | ||||||
|  | @ -1,8 +1,14 @@ | ||||||
| """ monkeypatching and mocking functionality.  """ | """ monkeypatching and mocking functionality.  """ | ||||||
| 
 | 
 | ||||||
| import os, sys | import os, sys | ||||||
|  | import re | ||||||
|  | 
 | ||||||
| from py.builtin import _basestring | from py.builtin import _basestring | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | RE_IMPORT_ERROR_NAME = re.compile("^No module named (.*)$") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| def pytest_funcarg__monkeypatch(request): | def pytest_funcarg__monkeypatch(request): | ||||||
|     """The returned ``monkeypatch`` funcarg provides these |     """The returned ``monkeypatch`` funcarg provides these | ||||||
|     helper methods to modify objects, dictionaries or os.environ:: |     helper methods to modify objects, dictionaries or os.environ:: | ||||||
|  | @ -34,14 +40,28 @@ def derive_importpath(import_path, raising): | ||||||
|                         (import_path,)) |                         (import_path,)) | ||||||
|     rest = [] |     rest = [] | ||||||
|     target = import_path |     target = import_path | ||||||
|  |     target_parts = set(target.split(".")) | ||||||
|     while target: |     while target: | ||||||
|         try: |         try: | ||||||
|             obj = __import__(target, None, None, "__doc__") |             obj = __import__(target, None, None, "__doc__") | ||||||
|         except ImportError: |         except ImportError as ex: | ||||||
|  |             if hasattr(ex, 'name'): | ||||||
|  |                 # Python >= 3.3 | ||||||
|  |                 failed_name = ex.name | ||||||
|  |             else: | ||||||
|  |                 match = RE_IMPORT_ERROR_NAME.match(ex.args[0]) | ||||||
|  |                 assert match | ||||||
|  |                 failed_name = match.group(1) | ||||||
|  | 
 | ||||||
|             if "." not in target: |             if "." not in target: | ||||||
|                 __tracebackhide__ = True |                 __tracebackhide__ = True | ||||||
|                 pytest.fail("could not import any sub part: %s" % |                 pytest.fail("could not import any sub part: %s" % | ||||||
|                             import_path) |                             import_path) | ||||||
|  |             elif failed_name != target \ | ||||||
|  |                     and not any(p == failed_name for p in target_parts): | ||||||
|  |                 # target is importable but causes ImportError itself | ||||||
|  |                 __tracebackhide__ = True | ||||||
|  |                 pytest.fail("import error in %s: %s" % (target, ex.args[0])) | ||||||
|             target, name = target.rsplit(".", 1) |             target, name = target.rsplit(".", 1) | ||||||
|             rest.append(name) |             rest.append(name) | ||||||
|         else: |         else: | ||||||
|  | @ -106,7 +126,7 @@ class monkeypatch: | ||||||
|         # avoid class descriptors like staticmethod/classmethod |         # avoid class descriptors like staticmethod/classmethod | ||||||
|         if inspect.isclass(target): |         if inspect.isclass(target): | ||||||
|             oldval = target.__dict__.get(name, notset) |             oldval = target.__dict__.get(name, notset) | ||||||
|         self._setattr.insert(0, (target, name, oldval)) |         self._setattr.append((target, name, oldval)) | ||||||
|         setattr(target, name, value) |         setattr(target, name, value) | ||||||
| 
 | 
 | ||||||
|     def delattr(self, target, name=notset, raising=True): |     def delattr(self, target, name=notset, raising=True): | ||||||
|  | @ -132,13 +152,12 @@ class monkeypatch: | ||||||
|             if raising: |             if raising: | ||||||
|                 raise AttributeError(name) |                 raise AttributeError(name) | ||||||
|         else: |         else: | ||||||
|             self._setattr.insert(0, (target, name, |             self._setattr.append((target, name, getattr(target, name, notset))) | ||||||
|                                      getattr(target, name, notset))) |  | ||||||
|             delattr(target, name) |             delattr(target, name) | ||||||
| 
 | 
 | ||||||
|     def setitem(self, dic, name, value): |     def setitem(self, dic, name, value): | ||||||
|         """ Set dictionary entry ``name`` to value. """ |         """ Set dictionary entry ``name`` to value. """ | ||||||
|         self._setitem.insert(0, (dic, name, dic.get(name, notset))) |         self._setitem.append((dic, name, dic.get(name, notset))) | ||||||
|         dic[name] = value |         dic[name] = value | ||||||
| 
 | 
 | ||||||
|     def delitem(self, dic, name, raising=True): |     def delitem(self, dic, name, raising=True): | ||||||
|  | @ -151,7 +170,7 @@ class monkeypatch: | ||||||
|             if raising: |             if raising: | ||||||
|                 raise KeyError(name) |                 raise KeyError(name) | ||||||
|         else: |         else: | ||||||
|             self._setitem.insert(0, (dic, name, dic.get(name, notset))) |             self._setitem.append((dic, name, dic.get(name, notset))) | ||||||
|             del dic[name] |             del dic[name] | ||||||
| 
 | 
 | ||||||
|     def setenv(self, name, value, prepend=None): |     def setenv(self, name, value, prepend=None): | ||||||
|  | @ -203,13 +222,13 @@ class monkeypatch: | ||||||
|         calling `undo()` will undo all of the changes made in |         calling `undo()` will undo all of the changes made in | ||||||
|         both functions. |         both functions. | ||||||
|         """ |         """ | ||||||
|         for obj, name, value in self._setattr: |         for obj, name, value in reversed(self._setattr): | ||||||
|             if value is not notset: |             if value is not notset: | ||||||
|                 setattr(obj, name, value) |                 setattr(obj, name, value) | ||||||
|             else: |             else: | ||||||
|                 delattr(obj, name) |                 delattr(obj, name) | ||||||
|         self._setattr[:] = [] |         self._setattr[:] = [] | ||||||
|         for dictionary, name, value in self._setitem: |         for dictionary, name, value in reversed(self._setitem): | ||||||
|             if value is notset: |             if value is notset: | ||||||
|                 try: |                 try: | ||||||
|                     del dictionary[name] |                     del dictionary[name] | ||||||
|  |  | ||||||
|  | @ -333,7 +333,7 @@ The result of this test will be successful:: | ||||||
| Parametrizing test methods through per-class configuration | Parametrizing test methods through per-class configuration | ||||||
| -------------------------------------------------------------- | -------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| .. _`unittest parametrizer`: http://code.google.com/p/unittest-ext/source/browse/trunk/params.py | .. _`unittest parametrizer`: https://github.com/testing-cabal/unittest-ext/blob/master/params.py | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Here is an example ``pytest_generate_function`` function implementing a | Here is an example ``pytest_generate_function`` function implementing a | ||||||
|  |  | ||||||
|  | @ -120,7 +120,7 @@ in a managed class/module/function scope. | ||||||
| 
 | 
 | ||||||
| .. _`Convention over Configuration`: http://en.wikipedia.org/wiki/Convention_over_Configuration | .. _`Convention over Configuration`: http://en.wikipedia.org/wiki/Convention_over_Configuration | ||||||
| 
 | 
 | ||||||
| Can I yield multiple values from a fixture function function? | Can I yield multiple values from a fixture function? | ||||||
| ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | ||||||
| 
 | 
 | ||||||
| There are two conceptual reasons why yielding from a factory function | There are two conceptual reasons why yielding from a factory function | ||||||
|  |  | ||||||
|  | @ -5,7 +5,6 @@ Getting started basics | ||||||
| .. toctree:: | .. toctree:: | ||||||
|    :maxdepth: 2 |    :maxdepth: 2 | ||||||
| 
 | 
 | ||||||
|    index |  | ||||||
|    getting-started |    getting-started | ||||||
|    usage |    usage | ||||||
|    goodpractises |    goodpractises | ||||||
|  |  | ||||||
|  | @ -18,6 +18,7 @@ def runandparse(testdir, *args): | ||||||
| 
 | 
 | ||||||
| def assert_attr(node, **kwargs): | def assert_attr(node, **kwargs): | ||||||
|     __tracebackhide__ = True |     __tracebackhide__ = True | ||||||
|  | 
 | ||||||
|     def nodeval(node, name): |     def nodeval(node, name): | ||||||
|         anode = node.getAttributeNode(name) |         anode = node.getAttributeNode(name) | ||||||
|         if anode is not None: |         if anode is not None: | ||||||
|  | @ -667,10 +668,13 @@ def test_runs_twice(testdir): | ||||||
|             pass |             pass | ||||||
|     ''') |     ''') | ||||||
| 
 | 
 | ||||||
|     result = testdir.runpytest(f, f, '--junitxml', testdir.tmpdir.join("test.xml")) |     result, dom = runandparse(testdir, f, f) | ||||||
|     assert 'INTERNALERROR' not in str(result.stdout) |     assert 'INTERNALERROR' not in result.stdout.str() | ||||||
|  |     first, second = [x['classname'] for x in dom.find_by_tag("testcase")] | ||||||
|  |     assert first == second | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @pytest.mark.xfail(reason='hangs', run=False) | ||||||
| def test_runs_twice_xdist(testdir): | def test_runs_twice_xdist(testdir): | ||||||
|     pytest.importorskip('xdist') |     pytest.importorskip('xdist') | ||||||
|     f = testdir.makepyfile(''' |     f = testdir.makepyfile(''' | ||||||
|  | @ -678,7 +682,60 @@ def test_runs_twice_xdist(testdir): | ||||||
|             pass |             pass | ||||||
|     ''') |     ''') | ||||||
| 
 | 
 | ||||||
|     result = testdir.runpytest(f, |     result, dom = runandparse( | ||||||
|         '--dist', 'each', '--tx', '2*popen', |         testdir, f, | ||||||
|         '--junitxml', testdir.tmpdir.join("test.xml")) |         '--dist', 'each', '--tx', '2*popen',) | ||||||
|     assert 'INTERNALERROR' not in str(result.stdout) |     assert 'INTERNALERROR' not in result.stdout.str() | ||||||
|  |     first, second = [x['classname'] for x in dom.find_by_tag("testcase")] | ||||||
|  |     assert first == second | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_fancy_items_regression(testdir): | ||||||
|  |     # issue 1259 | ||||||
|  |     testdir.makeconftest(""" | ||||||
|  |         import pytest | ||||||
|  |         class FunItem(pytest.Item): | ||||||
|  |             def runtest(self): | ||||||
|  |                 pass | ||||||
|  |         class NoFunItem(pytest.Item): | ||||||
|  |             def runtest(self): | ||||||
|  |                 pass | ||||||
|  | 
 | ||||||
|  |         class FunCollector(pytest.File): | ||||||
|  |             def collect(self): | ||||||
|  |                 return [ | ||||||
|  |                     FunItem('a', self), | ||||||
|  |                     NoFunItem('a', self), | ||||||
|  |                     NoFunItem('b', self), | ||||||
|  |                 ] | ||||||
|  | 
 | ||||||
|  |         def pytest_collect_file(path, parent): | ||||||
|  |             if path.check(ext='.py'): | ||||||
|  |                 return FunCollector(path, parent) | ||||||
|  |     """) | ||||||
|  | 
 | ||||||
|  |     testdir.makepyfile(''' | ||||||
|  |         def test_pass(): | ||||||
|  |             pass | ||||||
|  |     ''') | ||||||
|  | 
 | ||||||
|  |     result, dom = runandparse(testdir) | ||||||
|  | 
 | ||||||
|  |     assert 'INTERNALERROR' not in result.stdout.str() | ||||||
|  | 
 | ||||||
|  |     items = sorted( | ||||||
|  |         '%(classname)s %(name)s %(file)s' % x | ||||||
|  | 
 | ||||||
|  |         for x in dom.find_by_tag("testcase")) | ||||||
|  |     import pprint | ||||||
|  |     pprint.pprint(items) | ||||||
|  |     assert items == [ | ||||||
|  |         u'conftest a conftest.py', | ||||||
|  |         u'conftest a conftest.py', | ||||||
|  |         u'conftest b conftest.py', | ||||||
|  |         u'test_fancy_items_regression a test_fancy_items_regression.py', | ||||||
|  |         u'test_fancy_items_regression a test_fancy_items_regression.py', | ||||||
|  |         u'test_fancy_items_regression b test_fancy_items_regression.py', | ||||||
|  |         u'test_fancy_items_regression test_pass' | ||||||
|  |         u' test_fancy_items_regression.py', | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  | @ -1,4 +1,6 @@ | ||||||
| import os, sys | import os, sys | ||||||
|  | import textwrap | ||||||
|  | 
 | ||||||
| import pytest | import pytest | ||||||
| from _pytest.monkeypatch import monkeypatch as MonkeyPatch | from _pytest.monkeypatch import monkeypatch as MonkeyPatch | ||||||
| 
 | 
 | ||||||
|  | @ -245,6 +247,21 @@ def test_issue185_time_breaks(testdir): | ||||||
|         *1 passed* |         *1 passed* | ||||||
|     """) |     """) | ||||||
| 
 | 
 | ||||||
|  | def test_importerror(testdir): | ||||||
|  |     p = testdir.mkpydir("package") | ||||||
|  |     p.join("a.py").write(textwrap.dedent("""\ | ||||||
|  |         import doesnotexist | ||||||
|  | 
 | ||||||
|  |         x = 1 | ||||||
|  |     """)) | ||||||
|  |     testdir.tmpdir.join("test_importerror.py").write(textwrap.dedent("""\ | ||||||
|  |         def test_importerror(monkeypatch): | ||||||
|  |             monkeypatch.setattr('package.a.x', 2) | ||||||
|  |     """)) | ||||||
|  |     result = testdir.runpytest() | ||||||
|  |     result.stdout.fnmatch_lines(""" | ||||||
|  |         *import error in package.a.x: No module named {0}doesnotexist{0}* | ||||||
|  |     """.format("'" if sys.version_info > (3, 0) else "")) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class SampleNew(object): | class SampleNew(object): | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue