Compare commits

..

319 Commits
2.2.1 ... 2.3.4

Author SHA1 Message Date
holger krekel
b5955c5979 fix version number, final fixes 2012-11-20 14:01:31 +01:00
holger krekel
765b053984 bump version, add announcement, regen docs 2012-11-20 13:42:00 +01:00
holger krekel
a9adfa9114 don't run long-args test on windows because it can't work 2012-11-20 11:52:06 +01:00
holger krekel
7f403950ad adapt changelog entry about autouse fixtures and yield 2012-11-19 22:20:37 +01:00
holger krekel
f263f54889 make yielded tests participate in the autouse protocol 2012-11-19 22:17:59 +01:00
holger krekel
d66ff7e63e fix autouse invocation (off-by-one error), relates to issue in moinmoin test suite 2012-11-19 22:17:55 +01:00
holger krekel
f3e03fc298 modernize tmpdir fixture (use request.node in tmpdir fixture, use @pytest.fixture) 2012-11-19 14:07:14 +01:00
holger krekel
2ef350aede getting rid of redundant "active" attribute 2012-11-19 12:42:10 +01:00
holger krekel
b940ed11a0 fix issue226 - LIFO ordering for fixture-added teardowns 2012-11-16 10:03:51 +01:00
holger krekel
e15da7cbef add a note about yield tests at least in the CHANGELOG 2012-11-14 10:02:47 +01:00
holger krekel
5b64b0130d fix typo (thanks Thomas Waldmann) 2012-11-14 09:40:01 +01:00
holger krekel
af89a9667f add example for accessing test result information from fixture 2012-11-14 09:39:21 +01:00
holger krekel
c64c567b75 fix issue224 - invocations with >256 char arguments now work 2012-11-12 10:15:43 +01:00
ENDOH takanao
d31f4dcba8 Fix typos in a document 2012-11-10 16:29:43 +09:00
holger krekel
d9ce7f143e switch to pushing docs to dev, amend markers example which needs the dev candidate 2012-11-09 12:40:48 +01:00
holger krekel
4ac465acfb allow to pass expressions to "-k" option, just like with the "-m" option 2012-11-09 12:29:33 +01:00
holger krekel
a4909a0ae4 allow to dynamically define markers (e.g. during pytest_collection_modifyitems) 2012-11-09 12:07:41 +01:00
holger krekel
c790490387 add an example for postprocessing a test failure 2012-11-08 23:36:16 +01:00
holger krekel
664b01ca42 fix misleading typo 2012-11-08 19:05:46 +01:00
holger krekel
ff0c75aa34 - add a Package/dir level setup example
- make tox.ini's doc/regen use pytest release instead of dev version
2012-11-07 11:11:40 +01:00
holger krekel
476d210d09 prolong workaround for jython AST bug http://bugs.jython.org/issue1497
to make pytest work for post-2.5.1 jython versions
2012-11-07 10:05:39 +01:00
holger krekel
eedc4242ef mention that jython-2.5.1 works 2012-11-07 09:35:49 +01:00
holger krekel
370f5dd5cb fix typo 2012-11-06 15:46:52 +01:00
holger krekel
79f45928a4 add release announce for 2.3.3 2012-11-06 15:41:51 +01:00
holger krekel
dbff4ae034 Added tag 2.3.3 for changeset 7fe44182c434 2012-11-06 15:38:49 +01:00
holger krekel
d6f10d502c fix py31 compat, amend setup.py long description 2012-11-06 15:36:11 +01:00
holger krekel
65d6ebe7d1 bump to 2.3.3, add release announce 2012-11-06 14:41:10 +01:00
holger krekel
33cd414420 fix issue127 improve pytest_addoption docs, add new config.getoption(name) method for consistency. 2012-11-06 14:09:12 +01:00
holger krekel
dba2a8bc64 fix issue217 - to support @mock.patch with pytest funcarg-fixtures, also split out python integration tests into python/integration.py and fix nose/mark tests 2012-11-06 11:04:11 +01:00
holger krekel
f203401964 amend changelog entries 2012-11-06 09:27:58 +01:00
holger krekel
c64699bba6 fix issue219 - add trove classifiers for py24-py33 2012-11-06 09:14:41 +01:00
holger krekel
002c5072af addresses issue209 - avoid error messages from pip on python2.4 related to file, however, never be imported with this interpreter 2012-11-06 09:08:54 +01:00
Ronny Pfannschmidt
b3c8991b22 add a xfailing test for issue 199 2012-11-05 21:52:12 +01:00
Ronny Pfannschmidt
1a41c9c001 update changelog 2012-11-05 21:31:08 +01:00
Ronny Pfannschmidt
df444906d6 merge pull request 2012-11-05 21:18:50 +01:00
Ronny Pfannschmidt
04754f6748 test call_optional not calling non-callable functions 2012-11-05 21:17:58 +01:00
holger krekel
d5ad91c64f fix issue209 - depend on pylib dev version which again supports python2.4 2012-11-05 12:21:58 +01:00
holger krekel
7e831b66ec fix issue148 - recognize @unittest.skip on classes, avoid setup/teardown 2012-11-03 20:54:48 +01:00
holger krekel
ba9b27fcd3 fix issue215 - refactor test_python.py into multiple files:
- python/collect.py cotaining the core collection nodes
- python/fixture.py containing funcargs/fixture code
- python/metafunc.py generate_tests and metafunc usage
- python/raises.py the pytest.raises implementation
2012-11-02 16:04:57 +01:00
holger krekel
ca281b7c1b remove unused code 2012-11-02 16:04:56 +01:00
holger krekel
fb173a97a8 extended - fix issue214 - ignore attribute-access errors with objects in test modules that can blow up (for example flask's request object) 2012-10-31 17:00:55 +01:00
holger krekel
983b2d2475 merge 2012-10-31 17:01:24 +01:00
Ronny Pfannschmidt
e7e5ee805f fix issue 214 - gracefully handle proxy objects that look like fixtures 2012-10-31 17:00:43 +01:00
holger krekel
67f8dd0cf2 remove issue that doesn't make sense anymore 2012-10-28 17:40:30 +01:00
holger krekel
07cc48517d fix wrong reference in basic fixture example, thanks for reporting! (closes #212) 2012-10-28 14:54:49 +01:00
holger krekel
fce13c3e46 re-allow to parametrize with values that don't support __eq__ (closes issue213) 2012-10-28 14:52:43 +01:00
holger krekel
573599beb3 i think "helps you write better programs" fits better than "makes" 2012-10-28 11:25:53 +01:00
holger krekel
6ebf39e9a6 fix wrong document version on pytest.org (closes #210) 2012-10-28 10:13:37 +01:00
holger krekel
6b6080ae6c remove unused code 2012-10-28 10:12:36 +01:00
holger krekel
427cf6f66d add release announce 2012-10-25 14:13:43 +02:00
holger krekel
2fc8ee0839 Added tag 2.3.2 for changeset 8738b828dec5 2012-10-25 14:13:06 +02:00
holger krekel
6ad16936bb bump version to 2.3.2, regen docs and changelog 2012-10-25 13:48:31 +02:00
holger krekel
bcb8dc71d2 fix issue208 and fix issue29 - avoid long pauses in traceback printing
by using the new getstatementrange() code of the py lib which uses
AST-parsing rather than the previous heuristic which had O(n^2) complexity
(with n = len(sourcelines))

- require new (in-dev) py version to
2012-10-25 12:08:11 +02:00
holger krekel
b8277bfed8 fix issue127 - improve pytest_addoption and related documentation 2012-10-25 11:07:07 +02:00
holger krekel
2637326782 improve support for trial a bit more: don't run trial's empty TestCase.runTest() method 2012-10-22 19:22:01 +02:00
holger krekel
aa79c0a4b9 fix unittest emulation: TestCase.runTest is now ignored
if there are test* methods.
2012-10-22 16:25:09 +02:00
holger krekel
05c86aeb28 make sure ihook uses a node's fspath - important for hooks
e.g. during a Module's collect to pick up conftest.py files
residing in the same dir
2012-10-22 16:12:22 +02:00
holger krekel
f28f073c7c fix teardown-ordering for parametrized setups/teardowns 2012-10-22 12:16:54 +02:00
holger krekel
036557ac18 fix issue206 - unset PYTHONDONTWRITEBYTECODE in assertrewrite test 2012-10-22 11:14:18 +02:00
holger krekel
1b61fbc8ed - fix test_nose.py by being more tolerant about the error message
(differs between py32 and py33, thanks Arfrever)
- use pypi again now that py is released
2012-10-22 10:55:59 +02:00
holger krekel
97f03edcd6 fix issue205 - nested conftest to pickup pycollect_makemodule - relates to the two
reports of a failing doc/en/example/py2py3.
2012-10-22 10:17:50 +02:00
holger krekel
7e5efa0005 mention twisted with external plugins 2012-10-22 09:32:41 +02:00
holger krekel
d55fc611c4 properly handle non-existent PYTHONPATH 2012-10-20 17:39:15 +02:00
holger krekel
720fe3405b allow to run self-tests with "python setup.py test" for pytest tests itself 2012-10-20 17:32:03 +02:00
holger krekel
c894b2b459 add tox.ini to distribution 2012-10-20 17:08:02 +02:00
holger krekel
6d5bf4b908 Added tag 2.3.1 for changeset acf0e1477fb1 2012-10-20 14:33:23 +02:00
holger krekel
d4d213f83d some more fixes 2012-10-20 14:10:12 +02:00
holger krekel
289ee1c6ea prepare a 2.3.1 2012-10-20 14:05:33 +02:00
holger krekel
f41f7fda68 improve --markers output 2012-10-20 13:56:53 +02:00
holger krekel
9ed127b5da fix issue203 - fixture functions with a scope=function should have a "self" that points to the actual instance with which the test functions run. 2012-10-20 09:59:20 +02:00
holger krekel
525b08bc5c some doc refinements 2012-10-20 09:52:03 +02:00
holger krekel
fae34ca5e3 proper version number (2.3.1.dev*) 2012-10-19 16:00:29 +02:00
holger krekel
0852e84d9f skip pexpect using tests on freebsd 2012-10-19 15:59:29 +02:00
holger krekel
76db624639 start new dev cycle 2012-10-19 15:01:29 +02:00
holger krekel
1e6ec9941c Added tag 2.3.0 for changeset c27a60097767 2012-10-19 15:01:15 +02:00
holger krekel
a5ce481022 final touches 2012-10-19 11:12:13 +02:00
holger krekel
dca5fa2241 fixing links for 2.3 release, and fixing a windows32 failure on py3 2012-10-19 10:53:28 +02:00
holger krekel
586befb945 make usefixtures appear in py.test --markers output 2012-10-19 10:17:13 +02:00
holger krekel
b0b6695538 improve automatic id generation for parametrized tests 2012-10-19 10:07:13 +02:00
holger krekel
024df6e00b some more finalization of docs 2012-10-19 10:07:11 +02:00
holger krekel
5e28f461c8 avoid recursing into "ja" japanese examples 2012-10-18 15:32:30 +02:00
holger krekel
64544bee1a fix trial tests 2012-10-18 15:09:20 +02:00
holger krekel
7c8755cc89 refine docs, fix a marker/keywords bit, and add a test that request.keywords points to node.keywords. 2012-10-18 15:06:55 +02:00
holger krekel
7d747a1cde remove .markers attribute which was added in development and after 2.2.4
so never released.  Rather extend keywords to also exist on nodes. Assigning
to node.keywords will make the value appear on all subchildren's
keywords.
2012-10-18 13:52:32 +02:00
holger krekel
dbaedbacde many doc improvements and fixes 2012-10-18 12:24:50 +02:00
holger@merlinux.eu
cf17f1d628 fixing the fix of the last commit 2012-10-17 13:45:03 +02:00
holger krekel
67de2c53ac fix issue198 - detection of fixtures from conftest.py files in deeper nested dir structures with certain invocations 2012-10-17 13:42:40 +02:00
holger krekel
26ab80c4cd fix and test --fixtures location information 2012-10-17 13:12:33 +02:00
holger krekel
20849a44f5 improve --fixtures output with per-plugin grouping and hiding underscore names in non-verbose mode, re-introduce --funcargs for compatibiliy 2012-10-17 12:57:05 +02:00
holger krekel
51644a116c remove unused code 2012-10-17 11:50:32 +02:00
holger krekel
98513b995a simplify/integrate fixturemapper into FixtureManager
also fix jstests test failures
2012-10-17 11:20:45 +02:00
holger krekel
dc4e205876 typographic fixes, little simplification 2012-10-17 09:21:04 +02:00
holger krekel
2855a2f6cb remove outdated IMPL.txt and move up-to-date doc bits to FixtureMapper class. 2012-10-16 16:27:51 +02:00
holger krekel
cc2337af3a refine parsefactories interface, fix two_classes test originally reported by Alex Okrushko, also add a few more tests to make sure autouse-fixtures are properly distinguished 2012-10-16 16:13:12 +02:00
holger krekel
ab4183d400 strike another use of getfuncargnames() and rename FixtureDef.fixturenames to "argnames" because it's really just the fixture function arguments 2012-10-16 14:19:38 +02:00
holger krekel
37965657d0 make factorydeflist immutable by using an index 2012-10-16 13:59:12 +02:00
holger krekel
ccaa1af534 use FixtureInfo from FixtureRequest 2012-10-16 13:48:00 +02:00
holger krekel
2f3bbdafda use fixturemapper/fixtureinfo from Function objects 2012-10-16 13:48:00 +02:00
holger krekel
021c087701 implement fixture information stored on the parentnode of functions
to be reused by metafunc mechanics and Function setup
2012-10-16 13:47:59 +02:00
holger krekel
4541456a96 add plan for better fixture implementation, an xfailing test
and a slight refactoring of Metafunc tests/creation
2012-10-16 13:47:59 +02:00
holger krekel
f5d796b093 improve docs further, refine unittest docs, rename `autoactive to autouse`
to better match ``@pytest.mark.usefixtures`` naming.
2012-10-12 14:52:36 +02:00
ataumoefolau
40a55a640c nose.py: don't try to call setup if it's not callable 2012-10-12 14:39:17 +10:00
holger krekel
6eec2f5893 make tmpdir fixture always return a realpath()ed tmpdir and make a note
about it in the changed test.  Currently, i don't see a reason why this
is a bad idea (tm)
2012-10-11 13:05:16 +02:00
holger krekel
0594265adc fix output of --fixtures for @pytest.fixture defined functions. 2012-10-09 16:49:04 +02:00
holger krekel
fb3af07ef4 try to move docs to a more releasable state, also refine
release announce and a few coding bits
2012-10-09 14:35:17 +02:00
holger@merlinux.eu
39b8a19cf7 Fix test for windows 2012-10-08 13:42:31 +02:00
holger krekel
916c1c170e somewhat simplify pytest_generate_tests example 2012-10-08 13:19:31 +02:00
holger krekel
df643f65f0 remove support for @pytest.fixture on classes, to be reserved for future use:
Fixture-classes could offer setup/teardown/addoption/configure methods
and provide higher level support.  Preliminary allowing it to work on classes
may make introducing it harder.
2012-10-08 11:22:31 +02:00
holger krekel
d630d02c5b remove pytest.setup usage 2012-10-08 08:34:21 +02:00
holger krekel
30b10a6950 - fix doc references, refactor fixtures docs to more quickly start
with examples instead of big text blobgs
- also silence -q and -qq reporting some more
2012-10-07 13:06:17 +02:00
holger krekel
cda84fb566 - allow to use fixtures directly, i.e. without ()
- also allow scope to be determined by a dynamic function
2012-10-06 21:03:55 +02:00
holger krekel
d3893dd5d1 allow metafunc.parametrize(scope=...) calls to override the scope of a Fixture function definition. This is useful for cases where you want to dynamically
set scope and parametrization for a fixture instead of statically declaring
it on the fixture function.
2012-10-06 21:01:13 +02:00
holger krekel
55a8bfd174 fix issue197 - in case a function is parametrized with zero arguments,
skip it during setup
2012-10-06 11:34:06 +02:00
Floris Bruynooghe
f588eae4f5 Use updated names 2012-10-05 22:44:18 +01:00
holger krekel
d8c365ef2c implement pytest.mark.usefixtures and ini-file usefixtures setting
and also refine fixture docs a bit - fixtures.txt should now mostly
reflect the current state of the implementation
2012-10-05 19:20:40 +02:00
holger krekel
4cbb2ab3b3 bump version 2012-10-05 14:35:16 +02:00
holger krekel
d1a3f5c3a6 make the default non-error pass simpler and faster, refine error reporting by presenting "fixture" tracebacks 2012-10-05 14:24:45 +02:00
holger krekel
bb07ba7807 rename a number of internal and externally visible variables to use the fixture name
rather than funcargs.  Introduce .funcargnames compatibility attribute for backward compat.
2012-10-05 14:24:44 +02:00
holger krekel
8282efbb40 internally unify setup and fixture code, making setup a shortcut to fixture(autoactive=True) 2012-10-05 10:21:35 +02:00
holger krekel
9251e747af rename pytest.factory usages into pytest.fixture ones 2012-10-05 10:21:35 +02:00
holger krekel
439cc1238f merge factories/funcargs and setup functions into the new "fixture" document 2012-10-05 10:21:35 +02:00
holger krekel
3049af618c avoid pyc file issues by parametrizing the test instead of rewriting conftest.py files 2012-10-04 11:51:14 +02:00
holger krekel
7bc7a9b702 add py33 to tox.ini, report pypy-1.9 as working as well 2012-10-01 10:31:04 +02:00
holger krekel
5173647b4d fixes to against python3.3 2012-10-01 10:14:54 +02:00
holger krekel
57a832812b remove unneccessary internal __request__ funcarg. 2012-10-01 09:23:39 +02:00
Ronny Pfannschmidt
bee7543716 move Item.applymarker to Node, and defer to it from Funcargrequest.applymarker 2012-09-30 22:17:33 +02:00
holger krekel
b9767fd74c remove print, pass python32 2012-09-27 13:27:22 +02:00
holger krekel
dbe66f468a ensure proper calling of finalizers in case of parametrization on classes 2012-09-26 12:24:04 +02:00
Ronny Pfannschmidt
35cbb5791d fixes issue 156: monkeypatch class level descriptors 2012-09-25 18:15:13 +02:00
holger krekel
a18fd61a20 back out accidental changes introduced by last patch 2012-09-25 15:13:58 +02:00
holger krekel
a1c3d60747 add an xfail-ed test for a potential future "enabled" parameter to setup functions 2012-09-25 15:04:30 +02:00
holger krekel
fe4ccdff0e avoid double-instantiation of PluginManager in case of the "python pytest.py" or -m pytest invocation 2012-09-25 11:58:41 +02:00
holger krekel
cd1ead4f7b - make request.funcargnames carry the closure of all used funcargs
- make metafunc.funcargnames carry the closure of used funcargs
2012-09-24 17:04:34 +02:00
Ronny Pfannschmidt
9568ff3b23 backout, the _memoizedcall change worked only due to a local effect 2012-09-24 11:36:24 +02:00
Ronny Pfannschmidt
6e5f491a42 get rid of _memoizedcall - we dont really need it anymore 2012-09-24 11:26:38 +02:00
holger krekel
7768972ec5 make sure setups are called ahead of the funcarg factories of the test function 2012-09-24 10:36:22 +02:00
holger krekel
754fab9b55 merge 2012-09-22 20:26:13 +02:00
Ronny Pfannschmidt
253a87b2dc fix issue 191 - add support for runTest method of unittest.TestCase subclasses 2012-09-22 18:24:53 +02:00
holger krekel
81082ed3d3 extend --help to tell about --markers and --funcargs 2012-09-22 11:44:56 +02:00
holger krekel
465cfff6f9 don't call nose' setup methods if they are marked with pytest.setup 2012-09-22 00:23:36 +02:00
holger krekel
738f14a48a improve the parametrization scenario example to sort by id, rather than by file-order, see also: http://stackoverflow.com/questions/12521924/pytest-running-scenarios-in-the-correct-order-in-the-class 2012-09-21 09:39:54 +02:00
holger krekel
22dc47d9f9 refine internal test support for unicode-related bits (used by a test in pytest-pep8) 2012-09-20 10:57:23 +02:00
holger krekel
6cb3281ddd allow factory/setup-markers on classes, using their respective __init__ methods which can use the funcarg mechanism 2012-09-18 14:00:47 +02:00
holger krekel
a5e7e441d3 fix bug introduced with last checkin 2012-09-18 13:46:24 +02:00
holger krekel
a7c6688bd6 implement full @pytest.setup function unittest.TestCase interaction 2012-09-18 10:54:12 +02:00
holger krekel
d9c24552fc remove distinction of new versus old funcarg factories 2012-09-18 10:53:42 +02:00
holger krekel
631d311e89 - add request.node which maps to the collection node as specified by the scope.
- remove request.markers which is now available via request.node.markers
2012-09-17 20:43:37 +02:00
holger krekel
c2480f5c54 fix @funcarg to @factory 2012-09-17 17:36:08 +02:00
holger krekel
a94bb0a8bb introduce a new "markers" attribute to nodes and the request object. It is
a dynamic class making holdin
2012-09-17 17:32:23 +02:00
holger krekel
646c2c6001 drops special testcontext object in favour of "old" request object, simplifying communication and code for the 2.2-2.3 transition. also modify docs and examples. 2012-09-17 16:36:10 +02:00
holger krekel
f6b555f5ad merge 2012-09-17 08:41:04 +02:00
Ronny Pfannschmidt
bf5b226474 fix issue 188 - ensure sys.exc_info on py2 is clear before calling into a test 2012-09-15 15:20:49 +02:00
holger krekel
084c617b67 modify detection of factories located in plugins, allowing pytest's own test functions to access plugin defined funcargs even if they use internal machinery instead of a full test run 2012-09-12 12:51:45 +02:00
Ronny Pfannschmidt
bfaf8e50b6 fix issue 182: testdir.inprocess_run now considers passed plugins 2012-09-03 10:12:30 +02:00
Ronny Pfannschmidt
848c749d1a adapt the junit xml escaping test to my escaping changes 2012-09-03 09:54:02 +02:00
holger krekel
41ad7dbae1 fix issue185 monkeypatching time.time does not cause pytest to fail 2012-09-01 09:58:10 +02:00
holger krekel
93eac240a0 merge 2012-09-01 09:59:11 +02:00
Benjamin Peterson
a6060dfb6d use py3 compatible print syntax 2012-08-28 16:37:43 -04:00
Benjamin Peterson
7f36649763 remove usage of exception module, which is gone in py3.3 2012-08-28 16:35:06 -04:00
holger krekel
f07ebc6615 add talk from brianna and me from 2012 2012-08-26 16:30:01 +02:00
Ronny Pfannschmidt
e876ad9abd fix issue 179 - propperly show the dependency chain of factories on setup failure 2012-08-22 21:43:42 +02:00
Ronny Pfannschmidt
503addbf09 correctly have the test for issue #[C179 actually fail 2012-08-22 21:20:18 +02:00
Ronny Pfannschmidt
1318df4f5b add xfailing test for issue 179 2012-08-22 19:49:50 +02:00
Ronny Pfannschmidt
45693c2847 exchange the rawcode factory marker check with a more robust and specific instance check as advised by holger 2012-08-19 14:57:07 +02:00
Ronny Pfannschmidt
0e8cd9297a fix issue 176: raises(AssertionError) now catches builtin AssertionError as well 2012-08-19 13:45:26 +02:00
Ronny Pfannschmidt
0cca20bef9 ignore magic callables with no sane code in factory/setup discovery 2012-08-19 12:36:49 +02:00
Ronny Pfannschmidt
1446b4b4e6 fix issue #178 and extend the failure escape test 2012-08-17 16:08:08 +02:00
holger krekel
aa84359bd9 Merged in pfctdayelise/pytest (pull request #17) 2012-08-16 13:23:42 +02:00
Brianna Laugher
f275830ca7 expand list of projects based on URLs from holger 2012-08-16 19:31:21 +10:00
holger krekel
627e068516 fix issue172 so that @pytest.setup marked setup_module/function... functions
are not called twice.  Also fix ordering to that broader scoped setup
functions are executed first.
2012-08-13 13:37:14 +02:00
holger krekel
f472f21406 fix/update some docs to work with @pytest.factory instead of pytest_funcarg__ naming. 2012-08-13 12:58:08 +02:00
holger krekel
f4963270c6 fix typos 2012-08-11 20:02:34 +02:00
Brianna Laugher
08c3b1b80f Fix URL for waskr project 2012-08-10 15:44:58 +10:00
holger krekel
935761f098 also improve missing funcarg error for setup functions 2012-08-08 14:53:47 +02:00
holger krekel
dd268c1b2b improve error representation for missing factory definitions
in recursive funcarg reconstruction
2012-08-08 11:48:53 +02:00
holger krekel
172505f703 fix/consolidate --junitxml=path construction with relative pathes 2012-08-04 10:33:43 +02:00
holger krekel
6746a00cb8 majorly refine funcargs docs and rename "resources.txt" to "funcargs.txt" so that existing web links will eventually land at this new page when pytest is released. Also integrated the detailed reasoning and update setup function docs
to reflect latest discussions and feedback gathered on py-dev mailing list.
2012-08-03 19:08:27 +02:00
holger krekel
46dc7eeacb move pytest.mark.factory/setup to pytest.factory/setup, as per flub 's suggestion 2012-08-02 12:41:46 +02:00
holger krekel
ae241a5071 refine documentation, move setup to own "setup" page and provide
some more examples. move old setup_module/... to xunit_old page.
2012-08-02 12:07:54 +02:00
holger krekel
5fd84c35dd reshuffle docs, try to get a bit closer to release-relevant documentation 2012-08-01 14:52:51 +02:00
holger krekel
535d892f27 - rename @funcarg to @factory
- introduce a "testcontext" object for new-style funcargs and setup methods
- New-style funcargs and setup methods cannot use the "request" object anymore.
2012-08-01 13:57:09 +02:00
holger krekel
cb2eb9ba33 reorder internal layout so that funcarg-related functionality is in python.py 2012-08-01 09:23:39 +02:00
holger krekel
4f94ab4e42 mark a test as xfailing on python2.5 2012-08-01 09:10:40 +02:00
holger krekel
449b55cc70 - enhance ordering of tests using parametrized resources
- introduce a refined way to perform finalization for setup functions
  which does not use cached_setup() anymore
2012-08-01 09:07:32 +02:00
holger krekel
9dc79fd187 introduce a funcargcall object, holding meta information 2012-07-30 12:39:45 +02:00
holger krekel
b57fb9fd47 introduce a SetupCall, holding meta information and setup calling state 2012-07-30 11:51:50 +02:00
holger krekel
d68c65b493 minimize active parametrized non-function scoped resources by
- re-ordering at collection time
- modifying setup/teardown
2012-07-30 10:46:03 +02:00
holger krekel
fa61927c6b introduce @pytest.mark.setup decorated function,
extend newexamples.txt and draft a V4 resources API doc.
2012-07-24 12:10:04 +02:00
holger krekel
d4a487c725 allow funcarg factories to receive funcargs 2012-07-23 10:55:09 +02:00
holger krekel
76584b53a1 clarify and add to sort-by-session-scoped parametrized resources example 2012-07-23 10:54:57 +02:00
holger krekel
6b0f0adf5b implement a scope/parametrized examples using the so-far new features
also fix a bug with scoping/parametrization
2012-07-20 14:16:50 +02:00
holger krekel
396045e53f allow registration of "funcarg" marked factories 2012-07-20 14:16:49 +02:00
holger krekel
80db25822c implement funcarg factory scope marker and ScopeMismatch detection 2012-07-20 14:16:46 +02:00
holger krekel
f358fe7154 extend Metafunc and write a pytest_generate_tests hook on the funcarg manager
which discovers factories
2012-07-20 14:16:46 +02:00
holger krekel
e14459d45c discover funcarg factories independently from request/Function items 2012-07-20 14:16:28 +02:00
holger krekel
4e4b507472 move funcarg factory to a new FuncargManager object at session level 2012-07-19 09:20:14 +02:00
holger krekel
c7ee6e71ab re-introduce the old 2.2.4 FuncargRequest implementation as it is a better
base for implementing the new funcarg/setup api. Also Un-optimize
funcargnames discovery for now.
2012-07-18 19:49:14 +02:00
holger krekel
4766497515 V3 draft of resource api 2012-07-16 11:11:26 +02:00
holger krekel
38b18c44e9 ci 2012-07-18 19:48:43 +02:00
Wes Turner
a73c27da13 DOC: typo in doc/en/goodpractices.txt ("pytest" -> "PyTest") 2012-07-18 01:01:37 -05:00
holger krekel
dbaf7ee9d0 v2 of resources API draft 2012-07-16 10:47:41 +02:00
holger krekel
7a90bed19b V1 of the resources API draft 2012-07-16 10:47:00 +02:00
holger krekel
8adac2878f put automatic funcarg_ API to Py*objects only, refine internal subclassing and initialisation logic 2012-07-16 10:46:44 +02:00
holger krekel
66ed2d123a add a little example on how to group test execution by parametrized resource 2012-07-14 12:06:58 +02:00
Johannes
b902c36bfc Fix typo in terminal help text 2012-07-12 17:00:48 +01:00
Benjamin Peterson
099ac1e1f4 cleanup test a bit 2012-07-07 08:01:44 -07:00
Floris Bruynooghe
1aca6c9d7c Fix extension of of cached re-written file
With PYTHONOPTIMIZE set this had the extension of "o" instead of ".pyo".
Fixes issue #168.
2012-07-07 16:09:53 +02:00
Ronny Pfannschmidt
fe24e01a03 merge 2012-07-07 13:21:45 +02:00
holger krekel
838e758cf7 exit with errno instead of always signalling success, thanks John Anderson 2012-07-07 07:40:51 +02:00
holger krekel
ddd4467fdd merge 2012-07-02 13:23:41 +02:00
holger krekel
5574e45749 fix issue165 - fix broken links in documentation, also point to stackoverflow from FAQ and contact page 2012-07-02 13:13:48 +02:00
Ronny Pfannschmidt
74e55493d1 test and implement showing verbose assert repr for py.test -vv 2012-06-27 17:26:55 +02:00
holger krekel
ecec653e98 fix issue151 - heuristcally lookup conftest files on all command line arguments, not just the first existing dir/file
you can install the corresponding pytest-2.3.dev2 via
pip install -i http:/pypi.testrun.org -U pytest
2012-06-26 21:56:03 +02:00
holger krekel
0ba0f91720 remove unused code 2012-06-26 20:28:09 +02:00
holger krekel
ea49993459 fix issue139 - make it possible to access funcargs from pytest_runtest_setup 2012-06-25 18:08:12 +02:00
holger krekel
b4b86159cd better name for the oejskit-compatibility-class. 2012-06-25 17:49:13 +02:00
holger krekel
91b6f2bda8 mid-scale refactoring to make request API available directly on items.
This commit was slightly tricky because i want to backward
compatibility especially for the oejskit plugin which
uses Funcarg-filling for non-Function objects.
2012-06-25 17:35:33 +02:00
holger krekel
227d847216 fix problem with unicode in writing failure representations to terminal, thanks ThomasWaldmann 2012-06-24 16:42:31 +02:00
holger krekel
6e0c30d67d fix skip/xfail confusion, reported and discussed on
http://stackoverflow.com/questions/11105828/in-py-test-when-i-explicitly-skip-a-test-that-is-marked-as-xfail-how-can-i-get
2012-06-23 11:32:32 +02:00
holger krekel
65cbf591d8 ignore .cache 2012-06-22 10:53:28 +02:00
holger krekel
e79a312b92 fix internal test setup failure 2012-06-21 11:30:10 +02:00
holger krekel
42d44bfd43 fix some pep8 issues, more to go ... is there a tool that helps with pep8-ifying? 2012-06-21 11:20:29 +02:00
holger krekel
ccc04b9fc4 some refinements to reporting and hook order 2012-06-21 11:07:22 +02:00
holger krekel
18306a4644 add header info: always report 3rd party plugins in test runs 2012-06-20 00:16:47 +02:00
holger krekel
1bbe1d086c fix issue160 a failing setup of an xfail-marked tests should
be reported as xfail (not xpass)
2012-06-19 23:48:39 +02:00
holger krekel
672919a8e2 fix faq once more to get rid of the strange "missed" bit. 2012-06-18 11:47:57 +02:00
holger krekel
f176ee3a1c (correction-commit for wrong previous changelog message)
fix issue159 -- improve http://pytest.org/latest/faq.html
especially with respect to the "magic" history, also mention
pytest-django, trial and unittest integration.
2012-06-17 11:01:14 +02:00
holger krekel
474b177da8 fix issue129 - improve http://pytest.org/latest/faq.html
especially with respect to the "magic" history, also mention
pytest-django, trial and unittest integration.
2012-06-17 10:59:30 +02:00
holger krekel
b2e87ce027 change pluginmanager.register API to raise ValueError if the plugin object or the name is already registered 2012-06-16 21:29:04 +02:00
holger krekel
2e163e4aae mention pep302 in docstring 2012-06-16 10:14:52 +02:00
holger krekel
63eacd9dd5 fix comment handling 2012-06-12 13:41:29 +02:00
holger krekel
b008e489ba fix ReST errors, increment doc-version and push to pytest.org 2012-06-11 16:24:42 +02:00
Floris Bruynooghe
d5078001c9 Don't use deprecated API in example 2012-06-11 13:24:30 +01:00
holger krekel
8b3ac3b03a also set release 2012-06-07 19:26:18 +02:00
holger krekel
6af20a5290 use doc-versions that can increment separately from the pytest version 2012-06-07 19:18:50 +02:00
holger krekel
eb1b1005ae added an example on how to do python2/python3 customized test collection 2012-06-07 12:39:53 +02:00
holger krekel
6fd57ec786 extend marker section with a platform example 2012-06-06 16:34:13 +02:00
t2y
03a814a859 added Japanese translation for 2.2.4 (79a5b776a6f3) 2012-06-06 08:58:02 +09:00
t2y
b4c2161e35 fixed to find the CHANGELOG's path 2012-06-06 08:54:35 +09:00
t2y
9198069739 added "docs/en" directory and moved 2012-06-06 08:52:53 +09:00
holger krekel
4d77653bb0 simplify activate_funcargs 2012-06-03 21:06:43 +02:00
holger krekel
3f17784386 fix issue128 - show captured output when capsys/capfd are in use 2012-06-03 21:01:27 +02:00
holger krekel
971f96468c fix py2py3 example tests 2012-06-03 16:10:10 +02:00
holger krekel
c11202b549 remove pyc file 2012-06-01 20:13:18 +02:00
holger krekel
42d63832b7 draft example for skipping py2 and py3 only tests on
a per-module level.
2012-05-23 23:40:41 +02:00
holger krekel
f5f3fe54d5 update examples with 2.2.4 version, ReST fixes 2012-05-22 18:30:34 +02:00
holger krekel
76ec623b22 Added tag 2.2.4 for changeset ad9fe504a371 2012-05-22 18:27:49 +02:00
holger krekel
69fc6987ad upgrade inlined distribute_setup.py 2012-05-22 17:24:43 +02:00
holger krekel
0790f7a75f fix issue 144 - wrong classname in junitxml 2012-05-22 17:18:04 +02:00
Ronny Pfannschmidt
db8fbe7661 skip test for pyo on pypy since pypy doesnt do pyo 2012-05-22 16:20:58 +02:00
Jonathan Peirce
91c41cd6b3 minor fix to docs/usage 2012-05-22 12:24:53 +01:00
holger krekel
1bf1cfd07a fix help string for --paste 2012-05-19 10:54:12 +02:00
holger krekel
51d94a4a6e use higher difference on timing 2012-05-18 13:56:49 +02:00
holger krekel
e18abfd013 fix issue143 - call unconfigure/sessionfinish always when
configure/sessionstart where called

use exitcode 4 (instead of 3 which signaled an internal error)
when an initial directory/file was not found
2012-05-17 23:11:23 +02:00
holger krekel
6c7ea8191f fix wrong release version 2012-05-17 15:44:18 +02:00
holger krekel
329dca42a7 add release announcement 2012-05-17 15:25:58 +02:00
holger krekel
0362aaba5a require py-1.4.8 2012-05-17 08:47:50 +02:00
holger krekel
948dea8bb4 bump version to next release 2012-05-17 08:46:49 +02:00
Ronny Pfannschmidt
6155e9139d hande the trial todo class by using repr 2012-05-10 01:38:13 +02:00
holger krekel
6dd8405aed bump version 2012-05-10 00:34:47 +02:00
Ronny Pfannschmidt
c076f4e789 switch pastebin to bpaste.net, fixes #141 2012-05-08 16:13:25 +02:00
Ronny Pfannschmidt
d32a132b51 add the fix for issue 140 to CHANGELOG 2012-05-08 14:15:23 +02:00
Ronny Pfannschmidt
0e3779b14f strip bound wrappers of class setup/tardown, fixes #140
on python3 im_func is replaced by __func__
2012-05-06 23:03:16 +02:00
Benjamin Peterson
fe1c35f8d0 prepend the assertion rewriting hook, so as not to break when builtin import is explicitly on sys.meta_path 2012-05-05 17:31:05 -04:00
Benjamin Peterson
b4588f1798 escape the % operator in string formatting 2012-05-03 13:49:30 -04:00
Benjamin Peterson
64c7c1be15 use non-hacky dynamic package import method 2012-04-27 17:51:50 -04:00
Benjamin Peterson
1c817aa7bd don't use octal syntax, since its not py2/py3 compatible 2012-04-18 11:26:44 -04:00
Ronny Pfannschmidt
d02eaa8881 fix a import strange loop that affects pypy test appsupport on python2.5 2012-04-13 12:41:02 +02:00
holger krekel
b92176024c write down some thoughts on parametrization - still not completely clear what the next stage should look like ... 2012-03-31 10:01:03 -07:00
holger krekel
1c746e0819 merge 2012-03-31 10:12:11 -07:00
Ronny Pfannschmidt
166aae4418 word change in the docs fixes #130 2012-03-22 08:26:37 +01:00
holger krekel
58933aac2a try to better handle @unittest.expectedFailure decorator 2012-03-19 22:53:52 -07:00
Benjamin Peterson
45aa4e5229 remove unused import 2012-03-19 20:04:55 -04:00
holger krekel
e643e99586 bump version number to dev version 2012-03-19 08:53:08 -07:00
holger krekel
9f6d6f630d walk through issues 2012-03-19 06:56:35 -07:00
Ronny Pfannschmidt
812ba87f37 add acomment so highlighting won\'t fail 2012-03-15 15:22:13 +01:00
Ronny Pfannschmidt
2b0887fa5f document integration with setuptools/distribute test command and tests_require 2012-03-15 15:15:21 +01:00
Ronny Pfannschmidt
ee8d2f9950 junitxml: use a exclusive match on the legal ranges of xml for binary escaping, fixes issue 126 2012-03-09 13:12:18 +01:00
holger krekel
51d29cf4c6 mention 2.2.3 in 2.2.2 release announce 2012-02-05 23:54:20 -05:00
holger krekel
e378496b24 Added tag 2.2.3 for changeset 3c11c5c9776f 2012-02-05 23:39:00 -05:00
holger krekel
4d21274a29 release 2.2.3 to fix package contents (2.2.2 contained too many files) 2012-02-05 23:38:31 -05:00
holger krekel
705442cf4e Added tag 2.2.2 for changeset 92b916483c1e 2012-02-05 18:37:31 -05:00
holger krekel
87b4cb283f regen docs / examples for 2.2.2 2012-02-05 18:33:04 -05:00
holger krekel
83505b790d preparing release 2.2.2 2012-02-05 17:32:01 -05:00
Ronny Pfannschmidt
2ca6d9f039 no longer check if indirect metafunc.parametrize params are funcarg names 2012-02-03 16:54:00 +01:00
Ronny Pfannschmidt
87b8769680 readd CHANGELOG, since i accidentially killed it with mq 2012-02-03 17:11:42 +01:00
Ronny Pfannschmidt
78e7d7aed0 more quit collectonly shows only files and the number of tests in them 2012-02-03 16:56:06 +01:00
Ronny Pfannschmidt
68b353be0d create session.items before preparse, so collectonly wont break on preparse fail, fixes #115 2012-02-03 16:33:32 +01:00
holger krekel
a756dc8106 fix issue118 - typo 2012-02-02 06:24:12 -05:00
holger krekel
604e27658c add CHANGELOG for last commit 2012-02-01 09:06:38 -05:00
holger krekel
dfa273dc25 fix issue177 - actually perform session scope finalization 2012-02-01 08:52:34 -05:00
Ronny Pfannschmidt
5263656df6 kwarg support for reports, so xdist can deserialized extended reports 2012-01-20 19:50:45 +01:00
holger krekel
d88fe07377 fix link to pdf from contents page 2012-01-19 20:53:21 +01:00
holger krekel
2e23057804 remove nonsennse part of commit related to "mp" shortcut.
I wonder if introducing "mp" as a shortcut to monkeypatch
is a good idea, actually :)
2012-01-06 20:40:14 +00:00
holger krekel
303f49a5ad bump version, mention "mp" also in the docs and changelog 2012-01-06 20:37:18 +00:00
Ralf Schmitt
adbbd164ff update documentation for the new monkeypatch.chdir method 2012-01-06 15:25:57 +01:00
Ralf Schmitt
93424b0f9c add monkeypatch.chdir method 2012-01-04 12:43:19 +01:00
Ralf Schmitt
fb7706d4c7 make sure calling undo a second time doesn't change sys.path 2012-01-04 12:42:23 +01:00
Ralf Schmitt
4131923c0f test that a second undo doesn't change sys.path
also use a 'mp' funcarg that restores sys.path and the current working
directory in preparation for the monkeypatch.chdir method.
2012-01-04 12:40:57 +01:00
holger krekel
7b95af2400 only run test_unittest.py for the twisted/trial config 2011-12-28 17:43:56 +00:00
holger krekel
eb6481c663 fix trial test failure and simplify todo->xfail conversion 2011-12-28 17:35:38 +00:00
holger krekel
c126cac98d fix unittest/marker integration 2011-12-28 16:47:08 +00:00
holger krekel
e3a8b1e062 bump version 2011-12-28 15:49:35 +00:00
holger krekel
fa6d5bd15b work around an apparent python2.4/python2.5 bug with subprocess.Popen,
causing jenkins failures.  Apparently "os.environ.popitem(name, None)"
is not the same as::

    try:
        del os.environ[name]
    except KeyError:
        pass
2011-12-28 15:49:13 +00:00
holger krekel
f2c8a837af fix issue106: allow parametrize to be applied per-class/per-module 2011-12-28 15:47:19 +00:00
holger krekel
ccc1b21ebd internally keep multiple applications of the same markers as separate
entities such that the new iter() API can iterate over pytest.mark
function attributes, getting all such applications.  See added example
for more info.
2011-12-28 15:47:18 +00:00
holger krekel
85f2a78005 fix wrongly committed line 2011-12-28 07:57:19 +00:00
holger krekel
e21202b730 fix Jenkins test failures 2011-12-27 21:03:15 +00:00
holger krekel
dc0535f7d5 fix typo, thanks jurko 2011-12-20 14:12:12 +00:00
holger krekel
f2791988f9 fix issue102: report more useful errors and hints for when a
test directory was renamed and some pyc/__pycache__ remain
2011-12-20 12:20:59 +00:00
Benjamin Peterson
8e83af1c33 fix spacing 2011-12-19 14:23:39 -05:00
Benjamin Peterson
268c051eba propogate current PYTHONPATH 2011-12-19 12:02:07 -05:00
Benjamin Peterson
03cb37b1eb use an exception more consistent across python versions 2011-12-19 11:56:22 -05:00
holger krekel
d5c3265763 fix issue101: wrong args to unittest.TestCase test function now
produce better output
2011-12-18 23:01:39 +00:00
holger krekel
13e0340350 use newer distribute_setup.py 2011-12-18 19:01:43 +00:00
holger krekel
5093d8b925 fix test to actually mean something useful (thanks Jurko) 2011-12-18 10:56:39 +00:00
holger krekel
40187ec9bb robustify monkeypatch 2011-12-16 22:41:23 +00:00
holger krekel
f5f8695587 add 2.2.1 announce to index 2011-12-16 12:12:23 +00:00
holger krekel
27f5213718 Added tag 2.2.1 for changeset 3da8cec6c532 2011-12-16 11:56:44 +00:00
282 changed files with 20495 additions and 4027 deletions

View File

@@ -15,7 +15,7 @@ syntax:glob
*.orig
*~
doc/_build
doc/*/_build
build/
dist/
*.egg-info
@@ -23,3 +23,5 @@ issue/
env/
3rdparty/
.tox
.cache
.coverage

View File

@@ -45,3 +45,11 @@ e5e1746a197f0398356a43fbe2eebac9690f795d 2.1.0
5864412c6f3c903384243bd315639d101d7ebc67 2.1.2
12a05d59249f80276e25fd8b96e8e545b1332b7a 2.1.3
1522710369337d96bf9568569d5f0ca9b38a74e0 2.2.0
3da8cec6c5326ed27c144c9b6d7a64a648370005 2.2.1
92b916483c1e65a80dc80e3f7816b39e84b36a4d 2.2.2
3c11c5c9776f3c678719161e96cc0a08169c1cb8 2.2.3
ad9fe504a371ad8eb613052d58f229aa66f53527 2.2.4
c27a60097767c16a54ae56d9669a77925b213b9b 2.3.0
acf0e1477fb19a1d35a4e40242b77fa6af32eb17 2.3.1
8738b828dec53937765db71951ef955cca4c51f6 2.3.2
7fe44182c434f8ac89149a3c340479872a5d5ccb 2.3.3

219
CHANGELOG
View File

@@ -1,3 +1,222 @@
Changes between 2.3.3 and 2.3.4
-----------------------------------
- yielded test functions will now have autouse-fixtures active but
cannot accept fixtures as funcargs - it's anyway recommended to
rather use the post-2.0 parametrize features instead of yield, see:
http://pytest.org/latest/example/parametrize.html
- fix autouse-issue where autouse-fixtures would not be discovered
if defined in a a/conftest.py file and tests in a/tests/test_some.py
- fix issue226 - LIFO ordering for fixture teardowns
- fix issue224 - invocations with >256 char arguments now work
- fix issue91 - add/discuss package/directory level setups in example
- allow to dynamically define markers via
item.keywords[...]=assignment integrating with "-m" option
- make "-k" accept an expressions the same as with "-m" so that one
can write: -k "name1 or name2" etc. This is a slight incompatibility
if you used special syntax like "TestClass.test_method" which you now
need to write as -k "TestClass and test_method" to match a certain
method in a certain test class.
Changes between 2.3.2 and 2.3.3
-----------------------------------
- fix issue214 - parse modules that contain special objects like e. g.
flask's request object which blows up on getattr access if no request
is active. thanks Thomas Waldmann.
- fix issue213 - allow to parametrize with values like numpy arrays that
do not support an __eq__ operator
- fix issue215 - split test_python.org into multiple files
- fix issue148 - @unittest.skip on classes is now recognized and avoids
calling setUpClass/tearDownClass, thanks Pavel Repin
- fix issue209 - reintroduce python2.4 support by depending on newer
pylib which re-introduced statement-finding for pre-AST interpreters
- nose support: only call setup if its a callable, thanks Andrew
Taumoefolau
- fix issue219 - add py2.4-3.3 classifiers to TROVE list
- in tracebacks *,** arg values are now shown next to normal arguments
(thanks Manuel Jacob)
- fix issue217 - support mock.patch with pytest's fixtures - note that
you need either mock-1.0.1 or the python3.3 builtin unittest.mock.
- fix issue127 - improve documentation for pytest_addoption() and
add a ``config.getoption(name)`` helper function for consistency.
Changes between 2.3.1 and 2.3.2
-----------------------------------
- fix issue208 and fix issue29 use new py version to avoid long pauses
when printing tracebacks in long modules
- fix issue205 - conftests in subdirs customizing
pytest_pycollect_makemodule and pytest_pycollect_makeitem
now work properly
- fix teardown-ordering for parametrized setups
- fix issue127 - better documentation for pytest_addoption
and related objects.
- fix unittest behaviour: TestCase.runtest only called if there are
test methods defined
- improve trial support: don't collect its empty
unittest.TestCase.runTest() method
- "python setup.py test" now works with pytest itself
- fix/improve internal/packaging related bits:
- exception message check of test_nose.py now passes on python33 as well
- issue206 - fix test_assertrewrite.py to work when a global
PYTHONDONTWRITEBYTECODE=1 is present
- add tox.ini to pytest distribution so that ignore-dirs and others config
bits are properly distributed for maintainers who run pytest-own tests
Changes between 2.3.0 and 2.3.1
-----------------------------------
- fix issue202 - fix regression: using "self" from fixture functions now
works as expected (it's the same "self" instance that a test method
which uses the fixture sees)
- skip pexpect using tests (test_pdb.py mostly) on freebsd* systems
due to pexpect not supporting it properly (hanging)
- link to web pages from --markers output which provides help for
pytest.mark.* usage.
Changes between 2.2.4 and 2.3.0
-----------------------------------
- fix issue202 - better automatic names for parametrized test functions
- fix issue139 - introduce @pytest.fixture which allows direct scoping
and parametrization of funcarg factories.
- fix issue198 - conftest fixtures were not found on windows32 in some
circumstances with nested directory structures due to path manipulation issues
- fix issue193 skip test functions with were parametrized with empty
parameter sets
- fix python3.3 compat, mostly reporting bits that previously depended
on dict ordering
- introduce re-ordering of tests by resource and parametrization setup
which takes precedence to the usual file-ordering
- fix issue185 monkeypatching time.time does not cause pytest to fail
- fix issue172 duplicate call of pytest.fixture decoratored setup_module
functions
- fix junitxml=path construction so that if tests change the
current working directory and the path is a relative path
it is constructed correctly from the original current working dir.
- fix "python setup.py test" example to cause a proper "errno" return
- fix issue165 - fix broken doc links and mention stackoverflow for FAQ
- catch unicode-issues when writing failure representations
to terminal to prevent the whole session from crashing
- fix xfail/skip confusion: a skip-mark or an imperative pytest.skip
will now take precedence before xfail-markers because we
can't determine xfail/xpass status in case of a skip. see also:
http://stackoverflow.com/questions/11105828/in-py-test-when-i-explicitly-skip-a-test-that-is-marked-as-xfail-how-can-i-get
- always report installed 3rd party plugins in the header of a test run
- fix issue160: a failing setup of an xfail-marked tests should
be reported as xfail (not xpass)
- fix issue128: show captured output when capsys/capfd are used
- fix issue179: propperly show the dependency chain of factories
- pluginmanager.register(...) now raises ValueError if the
plugin has been already registered or the name is taken
- fix issue159: improve http://pytest.org/latest/faq.html
especially with respect to the "magic" history, also mention
pytest-django, trial and unittest integration.
- make request.keywords and node.keywords writable. All descendant
collection nodes will see keyword values. Keywords are dictionaries
containing markers and other info.
- fix issue 178: xml binary escapes are now wrapped in py.xml.raw
- fix issue 176: correctly catch the builtin AssertionError
even when we replaced AssertionError with a subclass on the
python level
- factory discovery no longer fails with magic global callables
that provide no sane __code__ object (mock.call for example)
- fix issue 182: testdir.inprocess_run now considers passed plugins
- fix issue 188: ensure sys.exc_info is clear on python2
before calling into a test
- fix issue 191: add unittest TestCase runTest method support
- fix issue 156: monkeypatch correctly handles class level descriptors
- reporting refinements:
- pytest_report_header now receives a "startdir" so that
you can use startdir.bestrelpath(yourpath) to show
nice relative path
- allow plugins to implement both pytest_report_header and
pytest_sessionstart (sessionstart is invoked first).
- don't show deselected reason line if there is none
- py.test -vv will show all of assert comparisations instead of truncating
Changes between 2.2.3 and 2.2.4
-----------------------------------
- fix error message for rewritten assertions involving the % operator
- fix issue 126: correctly match all invalid xml characters for junitxml
binary escape
- fix issue with unittest: now @unittest.expectedFailure markers should
be processed correctly (you can also use @pytest.mark markers)
- document integration with the extended distribute/setuptools test commands
- fix issue 140: propperly get the real functions
of bound classmethods for setup/teardown_class
- fix issue #141: switch from the deceased paste.pocoo.org to bpaste.net
- fix issue #143: call unconfigure/sessionfinish always when
configure/sessionstart where called
- fix issue #144: better mangle test ids to junitxml classnames
- upgrade distribute_setup.py to 0.6.27
Changes between 2.2.2 and 2.2.3
----------------------------------------
- fix uploaded package to only include neccesary files
Changes between 2.2.1 and 2.2.2
----------------------------------------
- fix issue101: wrong args to unittest.TestCase test function now
produce better output
- fix issue102: report more useful errors and hints for when a
test directory was renamed and some pyc/__pycache__ remain
- fix issue106: allow parametrize to be applied multiple times
e.g. from module, class and at function level.
- fix issue107: actually perform session scope finalization
- don't check in parametrize if indirect parameters are funcarg names
- add chdir method to monkeypatch funcarg
- fix crash resulting from calling monkeypatch undo a second time
- fix issue115: make --collectonly robust against early failure
(missing files/directories)
- "-qq --collectonly" now shows only files and the number of tests in them
- "-q --collectonly" now shows test ids
- allow adding of attributes to test reports such that it also works
with distributed testing (no upgrade of pytest-xdist needed)
Changes between 2.2.0 and 2.2.1
----------------------------------------

View File

@@ -1,23 +1,71 @@
refine parametrize API in 2.2 series
improve / add to dependency/test resource injection
-------------------------------------------------------------
tags: critical feature 2.2
tags: wish feature docs
extend metafunc.parametrize to better support indirection
by specifying a setupfunc(request, val) which will _substitute_
the funcarg factory. Here is an example:
write up better examples showing the connection between
the two.
def setupdb(request, val):
refine parametrize API
-------------------------------------------------------------
tags: critical feature
extend metafunc.parametrize to directly support indirection, example:
def setupdb(request, config):
# setup "resource" based on test request and the values passed
# in to parametrize. setupfunc is called for each such value.
# you may use request.addfinalizer() or request.cached_setup ...
return db
return dynamic_setup_database(val)
@pytest.mark.parametrize("db", ["pg", "mysql"], setupfunc=setupdb)
def test_heavy_functional_test(db):
...
There would be no need to write funcarg factories for this example, only
to explain the attributes and functionality of "request".
There would be no need to write or explain funcarg factories and
their special __ syntax.
The examples and improvements should also show how to put the parametrize
decorator to a class, to a module or even to a directory. For the directory
part a conftest.py content like this::
pytestmark = [
@pytest.mark.parametrize_setup("db", ...),
]
probably makes sense in order to keep the declarative nature. This mirrors
the marker-mechanism with respect to a test module but puts it to a directory
scale.
When doing larger scoped parametrization it probably becomes neccessary
to allow parametrization to be ignored if the according parameter is not
used (currently any parametrized argument that is not present in a function will cause a ValueError). Example:
@pytest.mark.parametrize("db", ..., mustmatch=False)
means to not raise an error but simply ignore the parametrization
if the signature of a decorated function does not match. XXX is it
not sufficient to always allow non-matches?
allow parametrized attributes on classes
--------------------------------------------------
tags: wish 2.4
example:
@pytest.mark.parametrize_attr("db", setupfunc, [1,2,3], scope="class")
@pytest.mark.parametrize_attr("tmp", setupfunc, scope="...")
class TestMe:
def test_hello(self):
access self.db ...
this would run the test_hello() function three times with three
different values for self.db. This could also work with unittest/nose
style tests, i.e. it leverages existing test suites without needing
to rewrite them. Together with the previously mentioned setup_test()
maybe the setupfunc could be ommitted?
checks / deprecations for next release
---------------------------------------------------------------
@@ -46,21 +94,9 @@ appropriately to avoid this issue. Moreover/Alternatively, we could
record which implementations of a hook succeeded and only call their
teardown.
do early-teardown of test modules
-----------------------------------------
tags: feature 2.3
currently teardowns are called when the next tests is setup
except for the function/method level where interally
"teardown_exact" tears down immediately. Generalize
this to perform the "neccessary" teardown compared to
the "next" test item during teardown - this should
get rid of some irritations because otherwise e.g.
prints of teardown-code appear in the setup of the next test.
consider and document __init__ file usage in test directories
---------------------------------------------------------------
tags: bug 2.3 core
tags: bug core
Currently, a test module is imported with its fully qualified
package path, determined by checking __init__ files upwards.
@@ -75,7 +111,7 @@ certain scenarios makes sense.
relax requirement to have tests/testing contain an __init__
----------------------------------------------------------------
tags: feature 2.3
tags: feature
bb: http://bitbucket.org/hpk42/py-trunk/issue/64
A local test run of a "tests" directory may work
@@ -86,7 +122,7 @@ i.e. port the nose-logic of unloading a test module.
customize test function collection
-------------------------------------------------------
tags: feature 2.3
tags: feature
- introduce py.test.mark.nocollect for not considering a function for
test collection at all. maybe also introduce a py.test.mark.test to
@@ -95,7 +131,7 @@ tags: feature 2.3
introduce pytest.mark.importorskip
-------------------------------------------------------
tags: feature 2.3
tags: feature
in addition to the imperative pytest.importorskip also introduce
a pytest.mark.importorskip so that the test count is more correct.
@@ -103,7 +139,7 @@ a pytest.mark.importorskip so that the test count is more correct.
introduce py.test.mark.platform
-------------------------------------------------------
tags: feature 2.3
tags: feature
Introduce nice-to-spell platform-skipping, examples:
@@ -120,7 +156,7 @@ interpreter versions.
pytest.mark.xfail signature change
-------------------------------------------------------
tags: feature 2.3
tags: feature
change to pytest.mark.xfail(reason, (optional)condition)
to better implement the word meaning. It also signals
@@ -128,36 +164,28 @@ better that we always have some kind of an implementation
reason that can be formualated.
Compatibility? how to introduce a new name/keep compat?
introduce py.test.mark registration
-----------------------------------------
tags: feature 2.3
introduce a hook that allows to register a named mark decorator
with documentation and add "py.test --marks" to get
a list of available marks. Deprecate "dynamic" mark
definitions.
allow to non-intrusively apply skipfs/xfail/marks
---------------------------------------------------
tags: feature 2.3
tags: feature
use case: mark a module or directory structures
to be skipped on certain platforms (i.e. no import
attempt will be made).
consider introducing a hook/mechanism that allows to apply marks
from conftests or plugins.
from conftests or plugins. (See extended parametrization)
explicit referencing of conftest.py files
-----------------------------------------
tags: feature 2.3
tags: feature
allow to name conftest.py files (in sub directories) that should
be imported early, as to include command line options.
improve central py.test ini file
----------------------------------
tags: feature 2.3
tags: feature
introduce more declarative configuration options:
- (to-be-collected test directories)
@@ -168,7 +196,7 @@ introduce more declarative configuration options:
new documentation
----------------------------------
tags: feature 2.3
tags: feature
- logo py.test
- examples for unittest or functional testing
@@ -177,23 +205,15 @@ tags: feature 2.3
have imported module mismatch honour relative paths
--------------------------------------------------------
tags: bug 2.3
tags: bug
With 1.1.1 py.test fails at least on windows if an import
is relative and compared against an absolute conftest.py
path. Normalize.
call termination with small timeout
-------------------------------------------------
tags: feature 2.3
test: testing/pytest/dist/test_dsession.py - test_terminate_on_hanging_node
Call gateway group termination with a small timeout if available.
Should make dist-testing less likely to leave lost processes.
consider globals: py.test.ensuretemp and config
--------------------------------------------------------------
tags: experimental-wish 2.3
tags: experimental-wish
consider deprecating py.test.ensuretemp and py.test.config
to further reduce py.test globality. Also consider
@@ -202,7 +222,7 @@ a plugin rather than being there from the start.
consider allowing funcargs for setup methods
--------------------------------------------------------------
tags: experimental-wish 2.3
tags: experimental-wish
Users have expressed the wish to have funcargs available to setup
functions. Experiment with allowing funcargs there - it might
@@ -225,7 +245,7 @@ world.
consider pytest_addsyspath hook
-----------------------------------------
tags: 2.3
tags:
py.test could call a new pytest_addsyspath() in order to systematically
allow manipulation of sys.path and to inhibit it via --no-addsyspath
@@ -237,7 +257,7 @@ and pytest_configure.
show plugin information in test header
----------------------------------------------------------------
tags: feature 2.3
tags: feature
Now that external plugins are becoming more numerous
it would be useful to have external plugins along with
@@ -245,7 +265,7 @@ their versions displayed as a header line.
deprecate global py.test.config usage
----------------------------------------------------------------
tags: feature 2.3
tags: feature
py.test.ensuretemp and py.test.config are probably the last
objects containing global state. Often using them is not
@@ -255,7 +275,7 @@ as others.
remove deprecated bits in collect.py
-------------------------------------------------------------------
tags: feature 2.3
tags: feature
In an effort to further simplify code, review and remove deprecated bits
in collect.py. Probably good:
@@ -264,7 +284,7 @@ in collect.py. Probably good:
implement fslayout decorator
---------------------------------
tags: feature 2.3
tags: feature
Improve the way how tests can work with pre-made examples,
keeping the layout close to the test function:
@@ -278,9 +298,35 @@ keeping the layout close to the test function:
pass
""")
def test_run(pytester, fslayout):
p = fslayout.find("test_*.py")
p = fslayout.findone("test_*.py")
result = pytester.runpytest(p)
assert result.ret == 0
assert result.passed == 1
Another idea is to allow to define a full scenario including the run
in one content string::
runscenario("""
test_{TESTNAME}.py:
import pytest
@pytest.mark.xfail
def test_that_fails():
assert 0
@pytest.mark.skipif("True")
def test_hello():
pass
conftest.py:
import pytest
def pytest_runsetup_setup(item):
pytest.skip("abc")
runpytest -rsxX
*SKIP*{TESTNAME}*
*1 skipped*
""")
This could be run with at least three different ways to invoke pytest:
through the shell, through "python -m pytest" and inlined. As inlined
would be the fastest it could be run first (or "--fast" mode).

View File

@@ -2,6 +2,7 @@ include CHANGELOG
include README.txt
include setup.py
include distribute_setup.py
include tox.ini
include LICENSE
graft doc
graft testing

View File

@@ -1,2 +1,2 @@
#
__version__ = '2.2.1'
__version__ = '2.3.4'

View File

@@ -50,7 +50,7 @@ def pytest_configure(config):
hook = None
if mode == "rewrite":
hook = rewrite.AssertionRewritingHook()
sys.meta_path.append(hook)
sys.meta_path.insert(0, hook)
warn_about_missing_assertion(mode)
config._assertstate = AssertionState(config, mode)
config._assertstate.hook = hook
@@ -73,8 +73,12 @@ def pytest_runtest_setup(item):
def callbinrepr(op, left, right):
hook_result = item.ihook.pytest_assertrepr_compare(
config=item.config, op=op, left=left, right=right)
for new_expl in hook_result:
if new_expl:
# Don't include pageloads of data unless we are very verbose (-vv)
if len(''.join(new_expl[1:])) > 80*8 and item.config.option.verbose < 2:
new_expl[1:] = ['Detailed information too verbose, truncated']
res = '\n~'.join(new_expl)
if item.config.getvalue("assertmode") == "rewrite":
# The result will be fed back a python % formatting

View File

@@ -11,7 +11,7 @@ from _pytest.assertion import util
from _pytest.assertion.reinterpret import BuiltinAssertionError
if sys.platform.startswith("java") and sys.version_info < (2, 5, 2):
if sys.platform.startswith("java"):
# See http://bugs.jython.org/issue1497
_exprs = ("BoolOp", "BinOp", "UnaryOp", "Lambda", "IfExp", "Dict",
"ListComp", "GeneratorExp", "Yield", "Compare", "Call",

View File

@@ -1,8 +1,7 @@
import py
import sys, inspect
from compiler import parse, ast, pycodegen
from _pytest.assertion.util import format_explanation
from _pytest.assertion.reinterpret import BuiltinAssertionError
from _pytest.assertion.util import format_explanation, BuiltinAssertionError
passthroughex = py.builtin._sysex
@@ -527,10 +526,13 @@ if __name__ == '__main__':
# example:
def f():
return 5
def g():
return 3
def h(x):
return 'never'
check("f() * g() == 5")
check("not f()")
check("not (f() and g() or 0)")

View File

@@ -1,7 +1,6 @@
import sys
import py
BuiltinAssertionError = py.builtin.builtins.AssertionError
from _pytest.assertion.util import BuiltinAssertionError
class AssertionError(BuiltinAssertionError):
def __init__(self, *args):
@@ -45,4 +44,3 @@ if sys.version_info >= (2, 6) or (sys.platform.startswith("java")):
from _pytest.assertion.newinterpret import interpret as reinterpret
else:
reinterpret = reinterpret_old

View File

@@ -1,7 +1,6 @@
"""Rewrite assertion AST to produce nice error messages"""
import ast
import collections
import errno
import itertools
import imp
@@ -35,13 +34,13 @@ else:
PYTEST_TAG = "%s-%s%s-PYTEST" % (impl, ver[0], ver[1])
del ver, impl
PYC_EXT = ".py" + "c" if __debug__ else "o"
PYC_EXT = ".py" + (__debug__ and "c" or "o")
PYC_TAIL = "." + PYTEST_TAG + PYC_EXT
REWRITE_NEWLINES = sys.version_info[:2] != (2, 7) and sys.version_info < (3, 2)
class AssertionRewritingHook(object):
"""Import hook which rewrites asserts."""
"""PEP302 Import hook which rewrites asserts."""
def __init__(self):
self.session = None
@@ -96,7 +95,8 @@ class AssertionRewritingHook(object):
finally:
self.session = sess
else:
state.trace("matched test file (was specified on cmdline): %r" % (fn,))
state.trace("matched test file (was specified on cmdline): %r" %
(fn,))
# The requested module looks like a test file, so rewrite it. This is
# the most magical part of the process: load the source, rewrite the
# asserts, and load the rewritten source. We also cache the rewritten
@@ -122,14 +122,14 @@ class AssertionRewritingHook(object):
# because we're in a zip file.
write = False
elif e == errno.EACCES:
state.trace("read only directory: %r" % (fn_pypath.dirname,))
state.trace("read only directory: %r" % fn_pypath.dirname)
write = False
else:
raise
cache_name = fn_pypath.basename[:-3] + PYC_TAIL
pyc = os.path.join(cache_dir, cache_name)
# Notice that even if we're in a read-only directory, I'm going to check
# for a cached pyc. This may not be optimal...
# Notice that even if we're in a read-only directory, I'm going
# to check for a cached pyc. This may not be optimal...
co = _read_pyc(fn_pypath, pyc)
if co is None:
state.trace("rewriting %r" % (fn,))
@@ -161,10 +161,11 @@ class AssertionRewritingHook(object):
return sys.modules[name]
def _write_pyc(co, source_path, pyc):
# Technically, we don't have to have the same pyc format as (C)Python, since
# these "pycs" should never be seen by builtin import. However, there's
# little reason deviate, and I hope sometime to be able to use
# imp.load_compiled to load them. (See the comment in load_module above.)
# Technically, we don't have to have the same pyc format as
# (C)Python, since these "pycs" should never be seen by builtin
# import. However, there's little reason deviate, and I hope
# sometime to be able to use imp.load_compiled to load them. (See
# the comment in load_module above.)
mtime = int(source_path.mtime())
try:
fp = open(pyc, "wb")
@@ -241,9 +242,8 @@ def _read_pyc(source, pyc):
except EnvironmentError:
return None
# Check for invalid or out of date pyc file.
if (len(data) != 8 or
data[:4] != imp.get_magic() or
struct.unpack("<l", data[4:])[0] != mtime):
if (len(data) != 8 or data[:4] != imp.get_magic() or
struct.unpack("<l", data[4:])[0] != mtime):
return None
co = marshal.load(fp)
if not isinstance(co, types.CodeType):
@@ -281,35 +281,35 @@ def _call_reprcompare(ops, results, expls, each_obj):
unary_map = {
ast.Not : "not %s",
ast.Invert : "~%s",
ast.USub : "-%s",
ast.UAdd : "+%s"
ast.Not: "not %s",
ast.Invert: "~%s",
ast.USub: "-%s",
ast.UAdd: "+%s"
}
binop_map = {
ast.BitOr : "|",
ast.BitXor : "^",
ast.BitAnd : "&",
ast.LShift : "<<",
ast.RShift : ">>",
ast.Add : "+",
ast.Sub : "-",
ast.Mult : "*",
ast.Div : "/",
ast.FloorDiv : "//",
ast.Mod : "%",
ast.Eq : "==",
ast.NotEq : "!=",
ast.Lt : "<",
ast.LtE : "<=",
ast.Gt : ">",
ast.GtE : ">=",
ast.Pow : "**",
ast.Is : "is",
ast.IsNot : "is not",
ast.In : "in",
ast.NotIn : "not in"
ast.BitOr: "|",
ast.BitXor: "^",
ast.BitAnd: "&",
ast.LShift: "<<",
ast.RShift: ">>",
ast.Add: "+",
ast.Sub: "-",
ast.Mult: "*",
ast.Div: "/",
ast.FloorDiv: "//",
ast.Mod: "%%", # escaped for string formatting
ast.Eq: "==",
ast.NotEq: "!=",
ast.Lt: "<",
ast.LtE: "<=",
ast.Gt: ">",
ast.GtE: ">=",
ast.Pow: "**",
ast.Is: "is",
ast.IsNot: "is not",
ast.In: "in",
ast.NotIn: "not in"
}
@@ -342,7 +342,7 @@ class AssertionRewriter(ast.NodeVisitor):
lineno = 0
for item in mod.body:
if (expect_docstring and isinstance(item, ast.Expr) and
isinstance(item.value, ast.Str)):
isinstance(item.value, ast.Str)):
doc = item.value.s
if "PYTEST_DONT_REWRITE" in doc:
# The module has disabled assertion rewriting.
@@ -463,7 +463,8 @@ class AssertionRewriter(ast.NodeVisitor):
body.append(raise_)
# Clear temporary variables by setting them to None.
if self.variables:
variables = [ast.Name(name, ast.Store()) for name in self.variables]
variables = [ast.Name(name, ast.Store())
for name in self.variables]
clear = ast.Assign(variables, ast.Name("None", ast.Load()))
self.statements.append(clear)
# Fix line numbers.
@@ -549,7 +550,8 @@ class AssertionRewriter(ast.NodeVisitor):
new_kwarg, expl = self.visit(call.kwargs)
arg_expls.append("**" + expl)
expl = "%s(%s)" % (func_expl, ', '.join(arg_expls))
new_call = ast.Call(new_func, new_args, new_kwargs, new_star, new_kwarg)
new_call = ast.Call(new_func, new_args, new_kwargs,
new_star, new_kwarg)
res = self.assign(new_call)
res_expl = self.explanation_param(self.display(res))
outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl)

View File

@@ -2,6 +2,7 @@
import py
BuiltinAssertionError = py.builtin.builtins.AssertionError
# The _reprcompare attribute on the util module is used by the new assertion
# interpretation code and assertion rewriter to detect this plugin was
@@ -115,16 +116,11 @@ def assertrepr_compare(op, left, right):
excinfo = py.code.ExceptionInfo()
explanation = ['(pytest_assertion plugin: representation of '
'details failed. Probably an object has a faulty __repr__.)',
str(excinfo)
]
str(excinfo)]
if not explanation:
return None
# Don't include pageloads of data, should be configurable
if len(''.join(explanation)) > 80*8:
explanation = ['Detailed information too verbose, truncated']
return [summary] + explanation

View File

@@ -119,22 +119,20 @@ class CaptureManager:
return "", ""
def activate_funcargs(self, pyfuncitem):
if not hasattr(pyfuncitem, 'funcargs'):
return
assert not hasattr(self, '_capturing_funcargs')
self._capturing_funcargs = capturing_funcargs = []
for name, capfuncarg in pyfuncitem.funcargs.items():
if name in ('capsys', 'capfd'):
capturing_funcargs.append(capfuncarg)
capfuncarg._start()
funcargs = getattr(pyfuncitem, "funcargs", None)
if funcargs is not None:
for name, capfuncarg in funcargs.items():
if name in ('capsys', 'capfd'):
assert not hasattr(self, '_capturing_funcarg')
self._capturing_funcarg = capfuncarg
capfuncarg._start()
def deactivate_funcargs(self):
capturing_funcargs = getattr(self, '_capturing_funcargs', None)
if capturing_funcargs is not None:
while capturing_funcargs:
capfuncarg = capturing_funcargs.pop()
capfuncarg._finalize()
del self._capturing_funcargs
capturing_funcarg = getattr(self, '_capturing_funcarg', None)
if capturing_funcarg:
outerr = capturing_funcarg._finalize()
del self._capturing_funcarg
return outerr
def pytest_make_collect_report(self, __multicall__, collector):
method = self._getmethod(collector.config, collector.fspath)
@@ -169,9 +167,12 @@ class CaptureManager:
@pytest.mark.tryfirst
def pytest_runtest_makereport(self, __multicall__, item, call):
self.deactivate_funcargs()
funcarg_outerr = self.deactivate_funcargs()
rep = __multicall__.execute()
outerr = self.suspendcapture(item)
if funcarg_outerr is not None:
outerr = (outerr[0] + funcarg_outerr[0],
outerr[1] + funcarg_outerr[1])
if not rep.passed:
addouterr(rep, outerr)
if not rep.passed or rep.when == "teardown":
@@ -179,23 +180,29 @@ class CaptureManager:
item.outerr = outerr
return rep
error_capsysfderror = "cannot use capsys and capfd at the same time"
def pytest_funcarg__capsys(request):
"""enables capturing of writes to sys.stdout/sys.stderr and makes
captured output available via ``capsys.readouterr()`` method calls
which return a ``(out, err)`` tuple.
"""
return CaptureFuncarg(py.io.StdCapture)
if "capfd" in request._funcargs:
raise request.raiseerror(error_capsysfderror)
return CaptureFixture(py.io.StdCapture)
def pytest_funcarg__capfd(request):
"""enables capturing of writes to file descriptors 1 and 2 and makes
captured output available via ``capsys.readouterr()`` method calls
which return a ``(out, err)`` tuple.
"""
if "capsys" in request._funcargs:
request.raiseerror(error_capsysfderror)
if not hasattr(os, 'dup'):
py.test.skip("capfd funcarg needs os.dup")
return CaptureFuncarg(py.io.StdCaptureFD)
pytest.skip("capfd funcarg needs os.dup")
return CaptureFixture(py.io.StdCaptureFD)
class CaptureFuncarg:
class CaptureFixture:
def __init__(self, captureclass):
self.capture = captureclass(now=False)
@@ -204,8 +211,9 @@ class CaptureFuncarg:
def _finalize(self):
if hasattr(self, 'capture'):
self.capture.reset()
outerr = self.capture.reset()
del self.capture
return outerr
def readouterr(self):
return self.capture.readouterr()

View File

@@ -19,7 +19,7 @@ def pytest_unconfigure(config):
fin()
class Parser:
""" Parser for command line arguments. """
""" Parser for command line arguments and ini-file values. """
def __init__(self, usage=None, processopt=None):
self._anonymous = OptionGroup("custom options", parser=self)
@@ -35,15 +35,17 @@ class Parser:
if option.dest:
self._processopt(option)
def addnote(self, note):
self._notes.append(note)
def getgroup(self, name, description="", after=None):
""" get (or create) a named option Group.
:name: unique name of the option group.
:name: name of the option group.
:description: long description for --help output.
:after: name of other group, used for ordering --help output.
The returned group object has an ``addoption`` method with the same
signature as :py:func:`parser.addoption
<_pytest.config.Parser.addoption>` but will be shown in the
respective group in the output of ``pytest. --help``.
"""
for group in self._groups:
if group.name == name:
@@ -57,7 +59,19 @@ class Parser:
return group
def addoption(self, *opts, **attrs):
""" add an optparse-style option. """
""" register a command line option.
:opts: option names, can be short or long options.
:attrs: same attributes which the ``add_option()`` function of the
`optparse library
<http://docs.python.org/library/optparse.html#module-optparse>`_
accepts.
After command line parsing options are available on the pytest config
object via ``config.option.NAME`` where ``NAME`` is usually set
by passing a ``dest`` attribute, for example
``addoption("--long", dest="NAME", ...)``.
"""
self._anonymous.addoption(*opts, **attrs)
def parse(self, args):
@@ -78,7 +92,15 @@ class Parser:
return args
def addini(self, name, help, type=None, default=None):
""" add an ini-file option with the given name and description. """
""" register an ini-file option.
:name: name of the ini-variable
:type: type of the variable, can be ``pathlist``, ``args`` or ``linelist``.
:default: default value if no ini-file option exists but is queried.
The value of ini-variables can be retrieved via a call to
:py:func:`config.getini(name) <_pytest.config.Config.getini>`.
"""
assert type in (None, "pathlist", "args", "linelist")
self._inidict[name] = (help, type, default)
self._ininames.append(name)
@@ -154,20 +176,24 @@ class Conftest(object):
p = current.join(opt1[len(opt)+1:], abs=1)
self._confcutdir = p
break
for arg in args + [current]:
foundanchor = False
for arg in args:
if hasattr(arg, 'startswith') and arg.startswith("--"):
continue
anchor = current.join(arg, abs=1)
if anchor.check(): # we found some file object
self._path2confmods[None] = self.getconftestmodules(anchor)
# let's also consider test* dirs
if anchor.check(dir=1):
for x in anchor.listdir("test*"):
if x.check(dir=1):
self.getconftestmodules(x)
break
else:
assert 0, "no root of filesystem?"
if exists(anchor): # we found some file object
self._try_load_conftest(anchor)
foundanchor = True
if not foundanchor:
self._try_load_conftest(current)
def _try_load_conftest(self, anchor):
self._path2confmods[None] = self.getconftestmodules(anchor)
# let's also consider test* subdirs
if anchor.check(dir=1):
for x in anchor.listdir("test*"):
if x.check(dir=1):
self.getconftestmodules(x)
def getconftestmodules(self, path):
""" return a list of imported conftest modules for the given path. """
@@ -245,8 +271,8 @@ class CmdOptions(object):
class Config(object):
""" access to configuration values, pluginmanager and plugin hooks. """
def __init__(self, pluginmanager=None):
#: command line option values, usually added via parser.addoption(...)
#: or parser.getgroup(...).addoption(...) calls
#: access to command line option as attributes.
#: (deprecated), use :py:func:`getoption() <_pytest.config.Config.getoption>` instead
self.option = CmdOptions()
self._parser = Parser(
usage="usage: %prog [options] [file_or_dir] [file_or_dir] [...]",
@@ -258,6 +284,7 @@ class Config(object):
self._conftest = Conftest(onimport=self._onimportconftest)
self.hook = self.pluginmanager.hook
self._inicache = {}
self._opt2dest = {}
self._cleanup = []
@classmethod
@@ -278,6 +305,9 @@ class Config(object):
self.pluginmanager.consider_conftest(conftestmodule)
def _processopt(self, opt):
for name in opt._short_opts + opt._long_opts:
self._opt2dest[name] = opt.dest
if hasattr(opt, 'default') and opt.dest:
if not hasattr(self.option, opt.dest):
setattr(self.option, opt.dest, opt.default)
@@ -356,8 +386,9 @@ class Config(object):
x.append(line) # modifies the cached list inline
def getini(self, name):
""" return configuration value from an ini file. If the
specified name hasn't been registered through a prior ``parse.addini``
""" return configuration value from an :ref:`ini file <inifiles>`. If the
specified name hasn't been registered through a prior
:py:func:`parser.addini <pytest.config.Parser.addini>`
call (usually from a plugin), a ValueError is raised. """
try:
return self._inicache[name]
@@ -411,8 +442,22 @@ class Config(object):
self._checkconftest(name)
return self._conftest.rget(name, path)
def getoption(self, name):
""" return command line option value.
:arg name: name of the option. You may also specify
the literal ``--OPT`` option instead of the "dest" option name.
"""
name = self._opt2dest.get(name, name)
try:
return getattr(self.option, name)
except AttributeError:
raise ValueError("no option named %r" % (name,))
def getvalue(self, name, path=None):
""" return ``name`` value looked set from command line options.
""" return command line option value.
:arg name: name of the command line option
(deprecated) if we can't find the option also lookup
the name in a matching conftest file.
@@ -434,6 +479,11 @@ class Config(object):
except KeyError:
py.test.skip("no %r value found" %(name,))
def exists(path, ignore=EnvironmentError):
try:
return path.check()
except ignore:
return False
def getcfg(args, inibasenames):
args = [x for x in args if not str(x).startswith("-")]
@@ -444,20 +494,9 @@ def getcfg(args, inibasenames):
for base in arg.parts(reverse=True):
for inibasename in inibasenames:
p = base.join(inibasename)
if p.check():
if exists(p):
iniconfig = py.iniconfig.IniConfig(p)
if 'pytest' in iniconfig.sections:
return iniconfig['pytest']
return {}
def findupwards(current, basename):
current = py.path.local(current)
while 1:
p = current.join(basename)
if p.check():
return p
p = current.dirpath()
if p == current:
return
current = p

View File

@@ -79,10 +79,11 @@ class PluginManager(object):
self.import_plugin(spec)
def register(self, plugin, name=None, prepend=False):
assert not self.isregistered(plugin), plugin
if self._name2plugin.get(name, None) == -1:
return
name = name or getattr(plugin, '__name__', str(id(plugin)))
if name in self._name2plugin:
return False
if self.isregistered(plugin, name):
raise ValueError("Plugin already registered: %s=%s" %(name, plugin))
#self.trace("registering", name, plugin)
self._name2plugin[name] = plugin
self.call_plugin(plugin, "pytest_addhooks", {'pluginmanager': self})
@@ -321,13 +322,15 @@ def importplugin(importspec):
name = importspec
try:
mod = "_pytest." + name
return __import__(mod, None, None, '__doc__')
__import__(mod)
return sys.modules[mod]
except ImportError:
#e = py.std.sys.exc_info()[1]
#if str(e).find(name) == -1:
# raise
pass #
return __import__(importspec, None, None, '__doc__')
__import__(importspec)
return sys.modules[importspec]
class MultiCall:
""" execute a call into multiple python functions/methods. """
@@ -460,16 +463,15 @@ def _prepareconfig(args=None, plugins=None):
pluginmanager=_pluginmanager, args=args)
def main(args=None, plugins=None):
""" returned exit code integer, after an in-process testing run
with the given command line arguments, preloading an optional list
of passed in plugin objects. """
try:
config = _prepareconfig(args, plugins)
exitstatus = config.hook.pytest_cmdline_main(config=config)
except UsageError:
e = sys.exc_info()[1]
sys.stderr.write("ERROR: %s\n" %(e.args[0],))
exitstatus = 3
""" return exit code, after performing an in-process test run.
:arg args: list of command line arguments.
:arg plugins: list of plugin objects to be auto-registered during
initialization.
"""
config = _prepareconfig(args, plugins)
exitstatus = config.hook.pytest_cmdline_main(config=config)
return exitstatus
class UsageError(Exception):

View File

@@ -79,6 +79,8 @@ def showhelp(config):
tw.line() ; tw.line()
#tw.sep("=")
tw.line("to see available markers type: py.test --markers")
tw.line("to see available fixtures type: py.test --fixtures")
return
tw.line("conftest.py options:")

View File

@@ -23,8 +23,28 @@ def pytest_cmdline_preparse(config, args):
"""modify command line arguments before option parsing. """
def pytest_addoption(parser):
"""add optparse-style options and ini-style config values via calls
to ``parser.addoption`` and ``parser.addini(...)``.
"""register optparse-style options and ini-style config values.
This function must be implemented in a :ref:`plugin <pluginorder>` and is
called once at the beginning of a test run.
:arg parser: To add command line options, call
:py:func:`parser.addoption(...) <_pytest.config.Parser.addoption>`.
To add ini-file values call :py:func:`parser.addini(...)
<_pytest.config.Parser.addini>`.
Options can later be accessed through the
:py:class:`config <_pytest.config.Config>` object, respectively:
- :py:func:`config.getoption(name) <_pytest.config.Config.getoption>` to
retrieve the value of a command line option.
- :py:func:`config.getini(name) <_pytest.config.Config.getini>` to retrieve
a value read from an ini-style file.
The config object is passed around on many internal objects via the ``.config``
attribute or can be retrieved as the ``pytestconfig`` fixture or accessed
via (deprecated) ``pytest.config``.
"""
def pytest_cmdline_main(config):
@@ -33,7 +53,7 @@ def pytest_cmdline_main(config):
pytest_cmdline_main.firstresult = True
def pytest_configure(config):
""" called after command line options have been parsed.
""" called after command line options have been parsed
and all plugins and initial conftest files been loaded.
"""
@@ -193,7 +213,7 @@ def pytest_assertrepr_compare(config, op, left, right):
# hooks for influencing reporting (invoked from _pytest_terminal)
# -------------------------------------------------------------------------
def pytest_report_header(config):
def pytest_report_header(config, startdir):
""" return a string to be displayed as header info for terminal reporting."""
def pytest_report_teststatus(report):

254
_pytest/impl Normal file
View File

@@ -0,0 +1,254 @@
Sorting per-resource
-----------------------------
for any given set of items:
- collect items per session-scoped parametrized funcarg
- re-order until items no parametrizations are mixed
examples:
test()
test1(s1)
test1(s2)
test2()
test3(s1)
test3(s2)
gets sorted to:
test()
test2()
test1(s1)
test3(s1)
test1(s2)
test3(s2)
the new @setup functions
--------------------------------------
Consider a given @setup-marked function::
@pytest.mark.setup(maxscope=SCOPE)
def mysetup(request, arg1, arg2, ...)
...
request.addfinalizer(fin)
...
then FUNCARGSET denotes the set of (arg1, arg2, ...) funcargs and
all of its dependent funcargs. The mysetup function will execute
for any matching test item once per scope.
The scope is determined as the minimum scope of all scopes of the args
in FUNCARGSET and the given "maxscope".
If mysetup has been called and no finalizers have been called it is
called "active".
Furthermore the following rules apply:
- if an arg value in FUNCARGSET is about to be torn down, the
mysetup-registered finalizers will execute as well.
- There will never be two active mysetup invocations.
Example 1, session scope::
@pytest.mark.funcarg(scope="session", params=[1,2])
def db(request):
request.addfinalizer(db_finalize)
@pytest.mark.setup
def mysetup(request, db):
request.addfinalizer(mysetup_finalize)
...
And a given test module:
def test_something():
...
def test_otherthing():
pass
Here is what happens::
db(request) executes with request.param == 1
mysetup(request, db) executes
test_something() executes
test_otherthing() executes
mysetup_finalize() executes
db_finalize() executes
db(request) executes with request.param == 2
mysetup(request, db) executes
test_something() executes
test_otherthing() executes
mysetup_finalize() executes
db_finalize() executes
Example 2, session/function scope::
@pytest.mark.funcarg(scope="session", params=[1,2])
def db(request):
request.addfinalizer(db_finalize)
@pytest.mark.setup(scope="function")
def mysetup(request, db):
...
request.addfinalizer(mysetup_finalize)
...
And a given test module:
def test_something():
...
def test_otherthing():
pass
Here is what happens::
db(request) executes with request.param == 1
mysetup(request, db) executes
test_something() executes
mysetup_finalize() executes
mysetup(request, db) executes
test_otherthing() executes
mysetup_finalize() executes
db_finalize() executes
db(request) executes with request.param == 2
mysetup(request, db) executes
test_something() executes
mysetup_finalize() executes
mysetup(request, db) executes
test_otherthing() executes
mysetup_finalize() executes
db_finalize() executes
Example 3 - funcargs session-mix
----------------------------------------
Similar with funcargs, an example::
@pytest.mark.funcarg(scope="session", params=[1,2])
def db(request):
request.addfinalizer(db_finalize)
@pytest.mark.funcarg(scope="function")
def table(request, db):
...
request.addfinalizer(table_finalize)
...
And a given test module:
def test_something(table):
...
def test_otherthing(table):
pass
def test_thirdthing():
pass
Here is what happens::
db(request) executes with param == 1
table(request, db)
test_something(table)
table_finalize()
table(request, db)
test_otherthing(table)
table_finalize()
db_finalize
db(request) executes with param == 2
table(request, db)
test_something(table)
table_finalize()
table(request, db)
test_otherthing(table)
table_finalize()
db_finalize
test_thirdthing()
Data structures
--------------------
pytest internally maintains a dict of active funcargs with cache, param,
finalizer, (scopeitem?) information:
active_funcargs = dict()
if a parametrized "db" is activated:
active_funcargs["db"] = FuncargInfo(dbvalue, paramindex,
FuncargFinalize(...), scopeitem)
if a test is torn down and the next test requires a differently
parametrized "db":
for argname in item.callspec.params:
if argname in active_funcargs:
funcarginfo = active_funcargs[argname]
if funcarginfo.param != item.callspec.params[argname]:
funcarginfo.callfinalizer()
del node2funcarg[funcarginfo.scopeitem]
del active_funcargs[argname]
nodes_to_be_torn_down = ...
for node in nodes_to_be_torn_down:
if node in node2funcarg:
argname = node2funcarg[node]
active_funcargs[argname].callfinalizer()
del node2funcarg[node]
del active_funcargs[argname]
if a test is setup requiring a "db" funcarg:
if "db" in active_funcargs:
return active_funcargs["db"][0]
funcarginfo = setup_funcarg()
active_funcargs["db"] = funcarginfo
node2funcarg[funcarginfo.scopeitem] = "db"
Implementation plan for resources
------------------------------------------
1. Revert FuncargRequest to the old form, unmerge item/request
(done)
2. make funcarg factories be discovered at collection time
3. Introduce funcarg marker
4. Introduce funcarg scope parameter
5. Introduce funcarg parametrize parameter
6. make setup functions be discovered at collection time
7. (Introduce a pytest_fixture_protocol/setup_funcargs hook)
methods and data structures
--------------------------------
A FuncarcManager holds all information about funcarg definitions
including parametrization and scope definitions. It implements
a pytest_generate_tests hook which performs parametrization as appropriate.
as a simple example, let's consider a tree where a test function requires
a "abc" funcarg and its factory defines it as parametrized and scoped
for Modules. When collections hits the function item, it creates
the metafunc object, and calls funcargdb.pytest_generate_tests(metafunc)
which looks up available funcarg factories and their scope and parametrization.
This information is equivalent to what can be provided today directly
at the function site and it should thus be relatively straight forward
to implement the additional way of defining parametrization/scoping.
conftest loading:
each funcarg-factory will populate the session.funcargmanager
When a test item is collected, it grows a dictionary
(funcargname2factorycalllist). A factory lookup is performed
for each required funcarg. The resulting factory call is stored
with the item. If a function is parametrized multiple items are
created with respective factory calls. Else if a factory is parametrized
multiple items and calls to the factory function are created as well.
At setup time, an item populates a funcargs mapping, mapping names
to values. If a value is funcarg factories are queried for a given item
test functions and setup functions are put in a class
which looks up required funcarg factories.

View File

@@ -34,15 +34,21 @@ class Junit(py.xml.Namespace):
# this dynamically instead of hardcoding it. The spec range of valid
# chars is: Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD]
# | [#x10000-#x10FFFF]
_illegal_unichrs = [(0x00, 0x08), (0x0B, 0x0C), (0x0E, 0x19),
(0xD800, 0xDFFF), (0xFDD0, 0xFFFF)]
_illegal_ranges = [unicode("%s-%s") % (unichr(low), unichr(high))
for (low, high) in _illegal_unichrs
_legal_chars = (0x09, 0x0A, 0x0d)
_legal_ranges = (
(0x20, 0xD7FF),
(0xE000, 0xFFFD),
(0x10000, 0x10FFFF),
)
_legal_xml_re = [unicode("%s-%s") % (unichr(low), unichr(high))
for (low, high) in _legal_ranges
if low < sys.maxunicode]
illegal_xml_re = re.compile(unicode('[%s]') %
unicode('').join(_illegal_ranges))
del _illegal_unichrs
del _illegal_ranges
_legal_xml_re = [unichr(x) for x in _legal_chars] + _legal_xml_re
illegal_xml_re = re.compile(unicode('[^%s]') %
unicode('').join(_legal_xml_re))
del _legal_chars
del _legal_ranges
del _legal_xml_re
def bin_xml_escape(arg):
def repl(matchobj):
@@ -51,7 +57,7 @@ def bin_xml_escape(arg):
return unicode('#x%02X') % i
else:
return unicode('#x%04X') % i
return illegal_xml_re.sub(repl, py.xml.escape(arg))
return py.xml.raw(illegal_xml_re.sub(repl, py.xml.escape(arg)))
def pytest_addoption(parser):
group = parser.getgroup("terminal reporting")
@@ -75,19 +81,22 @@ def pytest_unconfigure(config):
config.pluginmanager.unregister(xml)
def mangle_testnames(names):
names = [x.replace(".py", "") for x in names if x != '()']
names[0] = names[0].replace("/", '.')
return names
class LogXML(object):
def __init__(self, logfile, prefix):
logfile = os.path.expanduser(os.path.expandvars(logfile))
self.logfile = os.path.normpath(logfile)
self.logfile = os.path.normpath(os.path.abspath(logfile))
self.prefix = prefix
self.tests = []
self.passed = self.skipped = 0
self.failed = self.errors = 0
def _opentestcase(self, report):
names = report.nodeid.split("::")
names[0] = names[0].replace("/", '.')
names = [x.replace(".py", "") for x in names if x != "()"]
names = mangle_testnames(report.nodeid.split("::"))
classnames = names[:-1]
if self.prefix:
classnames.insert(0, self.prefix)
@@ -105,7 +114,7 @@ class LogXML(object):
def append_failure(self, report):
#msg = str(report.longrepr.reprtraceback.extraline)
if "xfail" in report.keywords:
if hasattr(report, "wasxfail"):
self.append(
Junit.skipped(message="xfail-marked test passes unexpectedly"))
self.skipped += 1
@@ -139,8 +148,8 @@ class LogXML(object):
self.errors += 1
def append_skipped(self, report):
if "xfail" in report.keywords:
self.append(Junit.skipped(str(report.keywords['xfail']),
if hasattr(report, "wasxfail"):
self.append(Junit.skipped(str(report.wasxfail),
message="expected test failure"))
else:
filename, lineno, skipreason = report.longrepr

View File

@@ -2,7 +2,15 @@
import py
import pytest, _pytest
import inspect
import os, sys, imp
try:
from collections import MutableMapping as MappingMixin
except ImportError:
from UserDict import DictMixin as MappingMixin
from _pytest.mark import MarkInfo
tracebackcutdir = py.path.local(_pytest.__file__).dirpath()
# exitcodes for the command line
@@ -10,6 +18,7 @@ EXIT_OK = 0
EXIT_TESTSFAILED = 1
EXIT_INTERRUPTED = 2
EXIT_INTERNALERROR = 3
EXIT_USAGEERROR = 4
name_re = py.std.re.compile("^[a-zA-Z_]\w*$")
@@ -28,7 +37,6 @@ def pytest_addoption(parser):
group._addoption('--maxfail', metavar="num",
action="store", type="int", dest="maxfail", default=0,
help="exit after first num failures or errors.")
group._addoption('--strict', action="store_true",
help="run pytest in strict mode, warnings become errors.")
@@ -65,30 +73,34 @@ def wrap_session(config, doit):
session.exitstatus = EXIT_OK
initstate = 0
try:
config.pluginmanager.do_configure(config)
initstate = 1
config.hook.pytest_sessionstart(session=session)
initstate = 2
doit(config, session)
except pytest.UsageError:
raise
except KeyboardInterrupt:
excinfo = py.code.ExceptionInfo()
config.hook.pytest_keyboard_interrupt(excinfo=excinfo)
session.exitstatus = EXIT_INTERRUPTED
except:
excinfo = py.code.ExceptionInfo()
config.pluginmanager.notify_exception(excinfo, config.option)
session.exitstatus = EXIT_INTERNALERROR
if excinfo.errisinstance(SystemExit):
sys.stderr.write("mainloop: caught Spurious SystemExit!\n")
if initstate >= 2:
config.hook.pytest_sessionfinish(session=session,
exitstatus=session.exitstatus or (session._testsfailed and 1))
if not session.exitstatus and session._testsfailed:
session.exitstatus = EXIT_TESTSFAILED
if initstate >= 1:
config.pluginmanager.do_unconfigure(config)
try:
config.pluginmanager.do_configure(config)
initstate = 1
config.hook.pytest_sessionstart(session=session)
initstate = 2
doit(config, session)
except pytest.UsageError:
msg = sys.exc_info()[1].args[0]
sys.stderr.write("ERROR: %s\n" %(msg,))
session.exitstatus = EXIT_USAGEERROR
except KeyboardInterrupt:
excinfo = py.code.ExceptionInfo()
config.hook.pytest_keyboard_interrupt(excinfo=excinfo)
session.exitstatus = EXIT_INTERRUPTED
except:
excinfo = py.code.ExceptionInfo()
config.pluginmanager.notify_exception(excinfo, config.option)
session.exitstatus = EXIT_INTERNALERROR
if excinfo.errisinstance(SystemExit):
sys.stderr.write("mainloop: caught Spurious SystemExit!\n")
finally:
if initstate >= 2:
config.hook.pytest_sessionfinish(session=session,
exitstatus=session.exitstatus or (session._testsfailed and 1))
if not session.exitstatus and session._testsfailed:
session.exitstatus = EXIT_TESTSFAILED
if initstate >= 1:
config.pluginmanager.do_unconfigure(config)
return session.exitstatus
def pytest_cmdline_main(config):
@@ -106,11 +118,18 @@ def pytest_collection(session):
def pytest_runtestloop(session):
if session.config.option.collectonly:
return True
for i, item in enumerate(session.items):
def getnextitem(i):
# this is a function to avoid python2
# keeping sys.exc_info set when calling into a test
# python2 keeps sys.exc_info till the frame is left
try:
nextitem = session.items[i+1]
return session.items[i+1]
except IndexError:
nextitem = None
return None
for i, item in enumerate(session.items):
nextitem = getnextitem(i)
item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem)
if session.shouldstop:
raise session.Interrupted(session.shouldstop)
@@ -129,8 +148,10 @@ class HookProxy:
def __init__(self, fspath, config):
self.fspath = fspath
self.config = config
def __getattr__(self, name):
hookmethod = getattr(self.config.hook, name)
def call_matching_hooks(**kwargs):
plugins = self.config._getmatchingplugins(self.fspath)
return hookmethod.pcall(plugins, **kwargs)
@@ -138,31 +159,71 @@ class HookProxy:
def compatproperty(name):
def fget(self):
# deprecated - use pytest.name
return getattr(pytest, name)
return property(fget, None, None,
"deprecated attribute %r, use pytest.%s" % (name,name))
return property(fget)
class NodeKeywords(MappingMixin):
def __init__(self, node):
parent = node.parent
bases = parent and (parent.keywords._markers,) or ()
self._markers = type("dynmarker", bases, {node.name: True})
def __getitem__(self, key):
try:
return getattr(self._markers, key)
except AttributeError:
raise KeyError(key)
def __setitem__(self, key, value):
setattr(self._markers, key, value)
def __delitem__(self, key):
delattr(self._markers, key)
def __iter__(self):
return iter(self.keys())
def __len__(self):
return len(self.keys())
def keys(self):
return dir(self._markers)
class Node(object):
""" base class for all Nodes in the collection tree.
""" base class for Collector and Item the test collection tree.
Collector subclasses have children, Items are terminal nodes."""
def __init__(self, name, parent=None, config=None, session=None):
#: a unique name with the scope of the parent
#: a unique name within the scope of the parent node
self.name = name
#: the parent collector node.
self.parent = parent
#: the test config object
#: the pytest config object
self.config = config or parent.config
#: the collection this node is part of
#: the session this node is part of
self.session = session or parent.session
#: filesystem path where this node was collected from
#: filesystem path where this node was collected from (can be None)
self.fspath = getattr(parent, 'fspath', None)
self.ihook = self.session.gethookproxy(self.fspath)
self.keywords = {self.name: True}
#: keywords/markers collected from all scopes
self.keywords = NodeKeywords(self)
#self.extrainit()
@property
def ihook(self):
""" fspath sensitive hook proxy used to call pytest hooks"""
return self.session.gethookproxy(self.fspath)
#def extrainit(self):
# """"extra initialization after Node is initialized. Implemented
# by some subclasses. """
Module = compatproperty("Module")
Class = compatproperty("Class")
@@ -180,25 +241,28 @@ class Node(object):
return cls
def __repr__(self):
return "<%s %r>" %(self.__class__.__name__, getattr(self, 'name', None))
return "<%s %r>" %(self.__class__.__name__,
getattr(self, 'name', None))
# methods for ordering nodes
@property
def nodeid(self):
""" a ::-separated string denoting its collection tree address. """
try:
return self._nodeid
except AttributeError:
self._nodeid = x = self._makeid()
return x
def _makeid(self):
return self.parent.nodeid + "::" + self.name
def __eq__(self, other):
if not isinstance(other, Node):
return False
return self.__class__ == other.__class__ and \
self.name == other.name and self.parent == other.parent
return (self.__class__ == other.__class__ and
self.name == other.name and self.parent == other.parent)
def __ne__(self, other):
return not self == other
@@ -257,6 +321,9 @@ class Node(object):
pass
def _repr_failure_py(self, excinfo, style=None):
fm = self.session._fixturemanager
if excinfo.errisinstance(fm.FixtureLookupError):
return excinfo.value.formatrepr()
if self.config.option.fulltrace:
style="long"
else:
@@ -365,9 +432,9 @@ class Session(FSCollector):
__module__ = 'builtins' # for py3
def __init__(self, config):
super(Session, self).__init__(py.path.local(), parent=None,
config=config, session=self)
assert self.config.pluginmanager.register(self, name="session", prepend=True)
FSCollector.__init__(self, py.path.local(), parent=None,
config=config, session=self)
self.config.pluginmanager.register(self, name="session", prepend=True)
self._testsfailed = 0
self.shouldstop = False
self.trace = config.trace.root.get("collection")
@@ -378,7 +445,7 @@ class Session(FSCollector):
raise self.Interrupted(self.shouldstop)
def pytest_runtest_logreport(self, report):
if report.failed and 'xfail' not in getattr(report, 'keywords', []):
if report.failed and not hasattr(report, 'wasxfail'):
self._testsfailed += 1
maxfail = self.config.getvalue("maxfail")
if maxfail and self._testsfailed >= maxfail:
@@ -410,6 +477,7 @@ class Session(FSCollector):
self._notfound = []
self._initialpaths = set()
self._initialparts = []
self.items = items = []
for arg in args:
parts = self._parsearg(arg)
self._initialparts.append(parts)
@@ -425,7 +493,6 @@ class Session(FSCollector):
if not genitems:
return rep.result
else:
self.items = items = []
if rep.passed:
for node in rep.result:
self.items.extend(self.genitems(node))
@@ -453,7 +520,7 @@ class Session(FSCollector):
if path.check(dir=1):
assert not names, "invalid arg %r" %(arg,)
for path in path.visit(fil=lambda x: x.check(file=1),
rec=self._recurse, bf=True, sort=True):
rec=self._recurse, bf=True, sort=True):
for x in self._collectfile(path):
yield x
else:
@@ -465,13 +532,13 @@ class Session(FSCollector):
ihook = self.gethookproxy(path)
if not self.isinitpath(path):
if ihook.pytest_ignore_collect(path=path, config=self.config):
return ()
return ()
return ihook.pytest_collect_file(path=path, parent=self)
def _recurse(self, path):
ihook = self.gethookproxy(path.dirpath())
if ihook.pytest_ignore_collect(path=path, config=self.config):
return
return
for pat in self._norecursepatterns:
if path.check(fnmatch=pat):
return False
@@ -574,3 +641,12 @@ class Session(FSCollector):
for x in self.genitems(subnode):
yield x
node.ihook.pytest_collectreport(report=rep)
def getfslineno(obj):
# xxx let decorators etc specify a sane ordering
if hasattr(obj, 'place_as'):
obj = obj.place_as
fslineno = py.code.getfslineno(obj)
assert isinstance(fslineno[1], int), obj
return fslineno

View File

@@ -51,7 +51,7 @@ def pytest_collection_modifyitems(items, config):
remaining = []
deselected = []
for colitem in items:
if keywordexpr and skipbykeyword(colitem, keywordexpr):
if keywordexpr and not matchkeyword(colitem, keywordexpr):
deselected.append(colitem)
else:
if selectuntil:
@@ -72,45 +72,26 @@ class BoolDict:
def __getitem__(self, name):
return name in self._mydict
class SubstringDict:
def __init__(self, mydict):
self._mydict = mydict
def __getitem__(self, name):
for key in self._mydict:
if name in key:
return True
return False
def matchmark(colitem, matchexpr):
return eval(matchexpr, {}, BoolDict(colitem.obj.__dict__))
return eval(matchexpr, {}, BoolDict(colitem.keywords))
def matchkeyword(colitem, keywordexpr):
keywordexpr = keywordexpr.replace("-", "not ")
return eval(keywordexpr, {}, SubstringDict(colitem.keywords))
def pytest_configure(config):
if config.option.strict:
pytest.mark._config = config
def skipbykeyword(colitem, keywordexpr):
""" return True if they given keyword expression means to
skip this collector/item.
"""
if not keywordexpr:
return
itemkeywords = getkeywords(colitem)
for key in filter(None, keywordexpr.split()):
eor = key[:1] == '-'
if eor:
key = key[1:]
if not (eor ^ matchonekeyword(key, itemkeywords)):
return True
def getkeywords(node):
keywords = {}
while node is not None:
keywords.update(node.keywords)
node = node.parent
return keywords
def matchonekeyword(key, itemkeywords):
for elem in key.split("."):
for kw in itemkeywords:
if elem in kw:
break
else:
return False
return True
class MarkGenerator:
""" Factory for :class:`MarkDecorator` objects - exposed as
a ``py.test.mark`` singleton instance. Example::
@@ -191,8 +172,7 @@ class MarkDecorator:
holder = MarkInfo(self.markname, self.args, self.kwargs)
setattr(func, self.markname, holder)
else:
holder.kwargs.update(self.kwargs)
holder.args += self.args
holder.add(self.args, self.kwargs)
return func
kw = self.kwargs.copy()
kw.update(kwargs)
@@ -208,27 +188,20 @@ class MarkInfo:
self.args = args
#: keyword argument dictionary, empty if nothing specified
self.kwargs = kwargs
self._arglist = [(args, kwargs.copy())]
def __repr__(self):
return "<MarkInfo %r args=%r kwargs=%r>" % (
self.name, self.args, self.kwargs)
def pytest_itemcollected(item):
if not isinstance(item, pytest.Function):
return
try:
func = item.obj.__func__
except AttributeError:
func = getattr(item.obj, 'im_func', item.obj)
pyclasses = (pytest.Class, pytest.Module)
for node in item.listchain():
if isinstance(node, pyclasses):
marker = getattr(node.obj, 'pytestmark', None)
if marker is not None:
if isinstance(marker, list):
for mark in marker:
mark(func)
else:
marker(func)
node = node.parent
item.keywords.update(py.builtin._getfuncdict(func))
def add(self, args, kwargs):
""" add a MarkInfo with the given args and kwargs. """
self._arglist.append((args, kwargs))
self.args += args
self.kwargs.update(kwargs)
def __iter__(self):
""" yield MarkInfo objects each relating to a marking-call. """
for args, kwargs in self._arglist:
yield MarkInfo(self.name, args, kwargs)

View File

@@ -1,6 +1,6 @@
""" monkeypatching and mocking functionality. """
import os, sys
import os, sys, inspect
def pytest_funcarg__monkeypatch(request):
"""The returned ``monkeypatch`` funcarg provides these
@@ -13,6 +13,7 @@ def pytest_funcarg__monkeypatch(request):
monkeypatch.setenv(name, value, prepend=False)
monkeypatch.delenv(name, value, raising=True)
monkeypatch.syspath_prepend(path)
monkeypatch.chdir(path)
All modifications will be undone after the requesting
test function has finished. The ``raising``
@@ -30,6 +31,7 @@ class monkeypatch:
def __init__(self):
self._setattr = []
self._setitem = []
self._cwd = None
def setattr(self, obj, name, value, raising=True):
""" set attribute ``name`` on ``obj`` to ``value``, by default
@@ -37,6 +39,10 @@ class monkeypatch:
oldval = getattr(obj, name, notset)
if raising and oldval is notset:
raise AttributeError("%r has no attribute %r" %(obj, name))
# avoid class descriptors like staticmethod/classmethod
if inspect.isclass(obj):
oldval = obj.__dict__.get(name, notset)
self._setattr.insert(0, (obj, name, oldval))
setattr(obj, name, value)
@@ -83,6 +89,17 @@ class monkeypatch:
self._savesyspath = sys.path[:]
sys.path.insert(0, str(path))
def chdir(self, path):
""" change the current working directory to the specified path
path can be a string or a py.path.local object
"""
if self._cwd is None:
self._cwd = os.getcwd()
if hasattr(path, "chdir"):
path.chdir()
else:
os.chdir(path)
def undo(self):
""" undo previous changes. This call consumes the
undo stack. Calling it a second time has no effect unless
@@ -95,9 +112,17 @@ class monkeypatch:
self._setattr[:] = []
for dictionary, name, value in self._setitem:
if value is notset:
del dictionary[name]
try:
del dictionary[name]
except KeyError:
pass # was already deleted, so we have the desired state
else:
dictionary[name] = value
self._setitem[:] = []
if hasattr(self, '_savesyspath'):
sys.path[:] = self._savesyspath
del self._savesyspath
if self._cwd is not None:
os.chdir(self._cwd)
self._cwd = None

View File

@@ -41,7 +41,7 @@ def pytest_make_collect_report(collector):
def call_optional(obj, name):
method = getattr(obj, name, None)
if method:
if method is not None and not hasattr(method, "_pytestfixturefunction") and py.builtin.callable(method):
# If there's any problems allow the exception to raise rather than
# silently ignoring them
method()

View File

@@ -2,7 +2,7 @@
import py, sys
class url:
base = "http://paste.pocoo.org"
base = "http://bpaste.net"
xmlrpc = base + "/xmlrpc/"
show = base + "/show/"
@@ -11,7 +11,7 @@ def pytest_addoption(parser):
group._addoption('--pastebin', metavar="mode",
action='store', dest="pastebin", default=None,
type="choice", choices=['failed', 'all'],
help="send failed|all info to Pocoo pastebin service.")
help="send failed|all info to bpaste.net pastebin service.")
def pytest_configure(__multicall__, config):
import tempfile

View File

@@ -50,7 +50,7 @@ def pytest_make_collect_report(__multicall__, collector):
def pytest_runtest_makereport():
pytestPDB.item = None
class PdbInvoke:
@pytest.mark.tryfirst
def pytest_runtest_makereport(self, item, call, __multicall__):
@@ -59,7 +59,7 @@ class PdbInvoke:
call.excinfo.errisinstance(pytest.skip.Exception) or \
call.excinfo.errisinstance(py.std.bdb.BdbQuit):
return rep
if "xfail" in rep.keywords:
if hasattr(rep, "wasxfail"):
return rep
# we assume that the above execute() suspended capturing
# XXX we re-use the TerminalReporter's terminalwriter

View File

@@ -2,6 +2,7 @@
import py, pytest
import sys, os
import codecs
import re
import inspect
import time
@@ -244,8 +245,10 @@ class TmpTestdir:
ret = None
for name, value in items:
p = self.tmpdir.join(name).new(ext=ext)
source = py.builtin._totext(py.code.Source(value)).lstrip()
p.write(source.encode("utf-8"), "wb")
source = py.builtin._totext(py.code.Source(value)).strip()
content = source.encode("utf-8") # + "\n"
#content = content.rstrip() + "\n"
p.write(content, "wb")
if ret is None:
ret = p
return ret
@@ -318,7 +321,7 @@ class TmpTestdir:
# used from runner functional tests
item = self.getitem(source)
# the test class where we are called from wants to provide the runner
testclassinstance = py.builtin._getimself(self.request.function)
testclassinstance = self.request.instance
runner = testclassinstance.getrunner()
return runner(item)
@@ -355,7 +358,7 @@ class TmpTestdir:
if not plugins:
plugins = []
plugins.append(Collect())
ret = self.pytestmain(list(args), plugins=[Collect()])
ret = self.pytestmain(list(args), plugins=plugins)
reprec = rec[0]
reprec.ret = ret
assert len(rec) == 1
@@ -388,10 +391,12 @@ class TmpTestdir:
return config
def getitem(self, source, funcname="test_func"):
for item in self.getitems(source):
items = self.getitems(source)
for item in items:
if item.name == funcname:
return item
assert 0, "%r item not found in module:\n%s" %(funcname, source)
assert 0, "%r item not found in module:\n%s\nitems: %s" %(
funcname, source, items)
def getitems(self, source):
modcol = self.getmodulecol(source)
@@ -438,28 +443,35 @@ class TmpTestdir:
p1 = self.tmpdir.join("stdout")
p2 = self.tmpdir.join("stderr")
print_("running", cmdargs, "curdir=", py.path.local())
f1 = p1.open("wb")
f2 = p2.open("wb")
now = time.time()
popen = self.popen(cmdargs, stdout=f1, stderr=f2,
close_fds=(sys.platform != "win32"))
ret = popen.wait()
f1.close()
f2.close()
out = p1.read("rb")
out = getdecoded(out).splitlines()
err = p2.read("rb")
err = getdecoded(err).splitlines()
def dump_lines(lines, fp):
try:
for line in lines:
py.builtin.print_(line, file=fp)
except UnicodeEncodeError:
print("couldn't print to %s because of encoding" % (fp,))
dump_lines(out, sys.stdout)
dump_lines(err, sys.stderr)
f1 = codecs.open(str(p1), "w", encoding="utf8")
f2 = codecs.open(str(p2), "w", encoding="utf8")
try:
now = time.time()
popen = self.popen(cmdargs, stdout=f1, stderr=f2,
close_fds=(sys.platform != "win32"))
ret = popen.wait()
finally:
f1.close()
f2.close()
f1 = codecs.open(str(p1), "r", encoding="utf8")
f2 = codecs.open(str(p2), "r", encoding="utf8")
try:
out = f1.read().splitlines()
err = f2.read().splitlines()
finally:
f1.close()
f2.close()
self._dump_lines(out, sys.stdout)
self._dump_lines(err, sys.stderr)
return RunResult(ret, out, err, time.time()-now)
def _dump_lines(self, lines, fp):
try:
for line in lines:
py.builtin.print_(line, file=fp)
except UnicodeEncodeError:
print("couldn't print to %s because of encoding" % (fp,))
def runpybin(self, scriptname, *args):
fullargs = self._getpybinargs(scriptname) + args
return self.run(*fullargs)
@@ -520,6 +532,8 @@ class TmpTestdir:
pytest.skip("pypy-64 bit not supported")
if sys.platform == "darwin":
pytest.xfail("pexpect does not work reliably on darwin?!")
if sys.platform.startswith("freebsd"):
pytest.xfail("pexpect does not work reliably on freebsd")
logfile = self.tmpdir.join("spawn.out")
child = pexpect.spawn(cmd, logfile=logfile.open("w"))
child.timeout = expect_timeout

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,6 @@
""" (disabled by default) create result information in a plain text file. """
""" log machine-parseable test session result information in a plain
text file.
"""
import py

View File

@@ -1,6 +1,7 @@
""" basic collect and runtest protocol implementations """
import py, sys, time
import py, sys
from time import time
from py._code.code import TerminalRepr
def pytest_namespace():
@@ -47,6 +48,8 @@ def pytest_terminal_summary(terminalreporter):
def pytest_sessionstart(session):
session._setupstate = SetupState()
def pytest_sessionfinish(session):
session._setupstate.teardown_all()
class NodeInfo:
def __init__(self, location):
@@ -112,7 +115,7 @@ class CallInfo:
#: context of invocation: one of "setup", "call",
#: "teardown", "memocollect"
self.when = when
self.start = time.time()
self.start = time()
try:
try:
self.result = func()
@@ -121,7 +124,7 @@ class CallInfo:
except:
self.excinfo = py.code.ExceptionInfo()
finally:
self.stop = time.time()
self.stop = time()
def __repr__(self):
if self.excinfo:
@@ -141,6 +144,10 @@ def getslaveinfoline(node):
return s
class BaseReport(object):
def __init__(self, **kw):
self.__dict__.update(kw)
def toterminal(self, out):
longrepr = self.longrepr
if hasattr(self, 'node'):
@@ -148,7 +155,10 @@ class BaseReport(object):
if hasattr(longrepr, 'toterminal'):
longrepr.toterminal(out)
else:
out.line(str(longrepr))
try:
out.line(longrepr)
except UnicodeEncodeError:
out.line("<unprintable longrepr>")
passed = property(lambda x: x.outcome == "passed")
failed = property(lambda x: x.outcome == "failed")
@@ -190,7 +200,7 @@ class TestReport(BaseReport):
they fail).
"""
def __init__(self, nodeid, location,
keywords, outcome, longrepr, when, sections=(), duration=0):
keywords, outcome, longrepr, when, sections=(), duration=0, **extra):
#: normalized collection node id
self.nodeid = nodeid
@@ -219,6 +229,8 @@ class TestReport(BaseReport):
#: time it took to run just the test
self.duration = duration
self.__dict__.update(extra)
def __repr__(self):
return "<TestReport %r when=%r outcome=%r>" % (
self.nodeid, self.when, self.outcome)
@@ -226,9 +238,10 @@ class TestReport(BaseReport):
class TeardownErrorReport(BaseReport):
outcome = "failed"
when = "teardown"
def __init__(self, longrepr):
def __init__(self, longrepr, **extra):
self.longrepr = longrepr
self.sections = []
self.__dict__.update(extra)
def pytest_make_collect_report(collector):
call = CallInfo(collector._memocollect, "memocollect")
@@ -250,12 +263,13 @@ def pytest_make_collect_report(collector):
getattr(call, 'result', None))
class CollectReport(BaseReport):
def __init__(self, nodeid, outcome, longrepr, result, sections=()):
def __init__(self, nodeid, outcome, longrepr, result, sections=(), **extra):
self.nodeid = nodeid
self.outcome = outcome
self.longrepr = longrepr
self.result = result or []
self.sections = list(sections)
self.__dict__.update(extra)
@property
def location(self):
@@ -269,7 +283,7 @@ class CollectErrorRepr(TerminalRepr):
def __init__(self, msg):
self.longrepr = msg
def toterminal(self, out):
out.line(str(self.longrepr), red=True)
out.line(self.longrepr, red=True)
class SetupState(object):
""" shared state for setting up/tearing down test items or collectors. """
@@ -281,6 +295,8 @@ class SetupState(object):
""" attach a finalizer to the given colitem.
if colitem is None, this will add a finalizer that
is called at the end of teardown_all().
if colitem is a tuple, it will be used as a key
and needs an explicit call to _callfinalizers(key) later on.
"""
assert hasattr(finalizer, '__call__')
#assert colitem in self.stack
@@ -298,15 +314,17 @@ class SetupState(object):
def _teardown_with_finalization(self, colitem):
self._callfinalizers(colitem)
if colitem:
if hasattr(colitem, "teardown"):
colitem.teardown()
for colitem in self._finalizers:
assert colitem is None or colitem in self.stack
assert colitem is None or colitem in self.stack \
or isinstance(colitem, tuple)
def teardown_all(self):
while self.stack:
self._pop_and_teardown()
self._teardown_with_finalization(None)
for key in list(self._finalizers):
self._teardown_with_finalization(key)
assert not self._finalizers
def teardown_exact(self, item, nextitem):
@@ -391,7 +409,9 @@ skip.Exception = Skipped
def fail(msg="", pytrace=True):
""" explicitely fail an currently-executing test with the given Message.
if @pytrace is not True the msg represents the full failure information.
:arg pytrace: if false the msg represents the full failure information
and no python traceback will be reported.
"""
__tracebackhide__ = True
raise Failed(msg=msg, pytrace=pytrace)
@@ -406,9 +426,10 @@ def importorskip(modname, minversion=None):
__tracebackhide__ = True
compile(modname, '', 'eval') # to catch syntaxerrors
try:
mod = __import__(modname, None, None, ['__doc__'])
__import__(modname)
except ImportError:
py.test.skip("could not import %r" %(modname,))
mod = sys.modules[modname]
if minversion is None:
return mod
verattr = getattr(mod, '__version__', None)

View File

@@ -11,17 +11,18 @@ def pytest_addoption(parser):
def pytest_configure(config):
config.addinivalue_line("markers",
"skipif(*conditions): skip the given test function if evaluation "
"of all conditions has a True value. Evaluation happens within the "
"skipif(condition): skip the given test function if eval(condition) "
"results in a True value. Evaluation happens within the "
"module global context. Example: skipif('sys.platform == \"win32\"') "
"skips the test if we are on the win32 platform. "
"skips the test if we are on the win32 platform. see "
"http://pytest.org/latest/skipping.html"
)
config.addinivalue_line("markers",
"xfail(*conditions, reason=None, run=True): mark the the test function "
"as an expected failure. Optionally specify a reason and run=False "
"if you don't even want to execute the test function. Any positional "
"condition strings will be evaluated (like with skipif) and if one is "
"False the marker will not be applied."
"xfail(condition, reason=None, run=True): mark the the test function "
"as an expected failure if eval(condition) has a True value. "
"Optionally specify a reason for better reporting and run=False if "
"you don't even want to execute the test function. See "
"http://pytest.org/latest/skipping.html"
)
def pytest_namespace():
@@ -110,6 +111,7 @@ class MarkEvaluator:
return expl
@pytest.mark.tryfirst
def pytest_runtest_setup(item):
if not isinstance(item, pytest.Function):
return
@@ -132,6 +134,14 @@ def check_xfail_no_run(item):
def pytest_runtest_makereport(__multicall__, item, call):
if not isinstance(item, pytest.Function):
return
# unitttest special case, see setting of _unexpectedsuccess
if hasattr(item, '_unexpectedsuccess'):
rep = __multicall__.execute()
if rep.when == "call":
# we need to translate into how py.test encodes xpass
rep.wasxfail = "reason: " + repr(item._unexpectedsuccess)
rep.outcome = "failed"
return rep
if not (call.excinfo and
call.excinfo.errisinstance(py.test.xfail.Exception)):
evalxfail = getattr(item, '_evalxfail', None)
@@ -140,27 +150,27 @@ def pytest_runtest_makereport(__multicall__, item, call):
if call.excinfo and call.excinfo.errisinstance(py.test.xfail.Exception):
if not item.config.getvalue("runxfail"):
rep = __multicall__.execute()
rep.keywords['xfail'] = "reason: " + call.excinfo.value.msg
rep.wasxfail = "reason: " + call.excinfo.value.msg
rep.outcome = "skipped"
return rep
rep = __multicall__.execute()
evalxfail = item._evalxfail
if not item.config.option.runxfail:
if evalxfail.wasvalid() and evalxfail.istrue():
if call.excinfo:
rep.outcome = "skipped"
rep.keywords['xfail'] = evalxfail.getexplanation()
elif call.when == "call":
rep.outcome = "failed"
rep.keywords['xfail'] = evalxfail.getexplanation()
return rep
if 'xfail' in rep.keywords:
del rep.keywords['xfail']
if not rep.skipped:
if not item.config.option.runxfail:
if evalxfail.wasvalid() and evalxfail.istrue():
if call.excinfo:
rep.outcome = "skipped"
elif call.when == "call":
rep.outcome = "failed"
else:
return rep
rep.wasxfail = evalxfail.getexplanation()
return rep
return rep
# called by terminalreporter progress reporting
def pytest_report_teststatus(report):
if 'xfail' in report.keywords:
if hasattr(report, "wasxfail"):
if report.skipped:
return "xfailed", "x", "xfail"
elif report.failed:
@@ -207,7 +217,7 @@ def show_xfailed(terminalreporter, lines):
if xfailed:
for rep in xfailed:
pos = rep.nodeid
reason = rep.keywords['xfail']
reason = rep.wasxfail
lines.append("XFAIL %s" % (pos,))
if reason:
lines.append(" " + str(reason))
@@ -217,7 +227,7 @@ def show_xpassed(terminalreporter, lines):
if xpassed:
for rep in xpassed:
pos = rep.nodeid
reason = rep.keywords['xfail']
reason = rep.wasxfail
lines.append("XPASS %s %s" %(pos, reason))
def cached_eval(config, expr, d):

View File

@@ -2,7 +2,8 @@
This is a good source for looking at the various reporting hooks.
"""
import pytest, py
import pytest
import py
import sys
import os
@@ -11,7 +12,7 @@ def pytest_addoption(parser):
group._addoption('-v', '--verbose', action="count",
dest="verbose", default=0, help="increase verbosity."),
group._addoption('-q', '--quiet', action="count",
dest="quiet", default=0, help="decreate verbosity."),
dest="quiet", default=0, help="decrease verbosity."),
group._addoption('-r',
action="store", dest="reportchars", default=None, metavar="chars",
help="show extra test summary info as specified by chars (f)ailed, "
@@ -37,13 +38,14 @@ def pytest_configure(config):
stdout = py.std.sys.stdout
if hasattr(os, 'dup') and hasattr(stdout, 'fileno'):
try:
newfd = os.dup(stdout.fileno())
#print "got newfd", newfd
newstdout = py.io.dupfile(stdout, buffering=1,
encoding=stdout.encoding)
except ValueError:
pass
else:
stdout = os.fdopen(newfd, stdout.mode, 1)
config._cleanup.append(lambda: stdout.close())
config._cleanup.append(lambda: newstdout.close())
assert stdout.encoding == newstdout.encoding
stdout = newstdout
reporter = TerminalReporter(config, stdout)
config.pluginmanager.register(reporter, 'terminalreporter')
@@ -94,7 +96,7 @@ class TerminalReporter:
self._numcollected = 0
self.stats = {}
self.curdir = py.path.local()
self.startdir = self.curdir = py.path.local()
if file is None:
file = py.std.sys.stdout
self._tw = py.io.TerminalWriter(file)
@@ -109,9 +111,9 @@ class TerminalReporter:
def write_fspath_result(self, fspath, res):
if fspath != self.currentfspath:
self.currentfspath = fspath
#fspath = self.curdir.bestrelpath(fspath)
#fspath = self.startdir.bestrelpath(fspath)
self._tw.line()
#relpath = self.curdir.bestrelpath(fspath)
#relpath = self.startdir.bestrelpath(fspath)
self._tw.write(fspath + " ")
self._tw.write(res)
@@ -207,7 +209,7 @@ class TerminalReporter:
self.currentfspath = -2
def pytest_collection(self):
if not self.hasmarkup:
if not self.hasmarkup and self.config.option.verbose >=1:
self.write("collecting ... ", bold=True)
def pytest_collectreport(self, report):
@@ -222,6 +224,9 @@ class TerminalReporter:
self.report_collect()
def report_collect(self, final=False):
if self.config.option.verbose < 0:
return
errors = len(self.stats.get('error', []))
skipped = len(self.stats.get('skipped', []))
if final:
@@ -243,6 +248,7 @@ class TerminalReporter:
def pytest_collection_modifyitems(self):
self.report_collect(True)
@pytest.mark.trylast
def pytest_sessionstart(self, session):
self._sessionstarttime = py.std.time.time()
if not self.showheader:
@@ -258,11 +264,23 @@ class TerminalReporter:
getattr(self.config.option, 'pastebin', None):
msg += " -- " + str(sys.executable)
self.write_line(msg)
lines = self.config.hook.pytest_report_header(config=self.config)
lines = self.config.hook.pytest_report_header(
config=self.config, startdir=self.startdir)
lines.reverse()
for line in flatten(lines):
self.write_line(line)
def pytest_report_header(self, config):
plugininfo = config.pluginmanager._plugin_distinfo
if plugininfo:
l = []
for dist, plugin in plugininfo:
name = dist.project_name
if name.startswith("pytest-"):
name = name[7:]
l.append(name)
return "plugins: %s" % ", ".join(l)
def pytest_collection_finish(self, session):
if self.config.option.collectonly:
self._printcollecteditems(session.items)
@@ -282,10 +300,18 @@ class TerminalReporter:
# we take care to leave out Instances aka ()
# because later versions are going to get rid of them anyway
if self.config.option.verbose < 0:
for item in items:
nodeid = item.nodeid
nodeid = nodeid.replace("::()::", "::")
self._tw.line(nodeid)
if self.config.option.verbose < -1:
counts = {}
for item in items:
name = item.nodeid.split('::', 1)[0]
counts[name] = counts.get(name, 0) + 1
for name, count in sorted(counts.items()):
self._tw.line("%s: %d" % (name, count))
else:
for item in items:
nodeid = item.nodeid
nodeid = nodeid.replace("::()::", "::")
self._tw.line(nodeid)
return
stack = []
indent = ""
@@ -417,7 +443,7 @@ class TerminalReporter:
def summary_stats(self):
session_duration = py.std.time.time() - self._sessionstarttime
keys = "failed passed skipped deselected".split()
keys = "failed passed skipped deselected xfailed xpassed".split()
for key in self.stats.keys():
if key not in keys:
keys.append(key)
@@ -432,8 +458,8 @@ class TerminalReporter:
msg = "%s in %.2f seconds" %(line, session_duration)
if self.verbosity >= 0:
self.write_sep("=", msg, bold=True)
else:
self.write_line(msg, bold=True)
#else:
# self.write_line(msg, bold=True)
def summary_deselected(self):
if 'deselected' in self.stats:
@@ -444,8 +470,9 @@ class TerminalReporter:
m = self.config.option.markexpr
if m:
l.append("-m %r" % m)
self.write_sep("=", "%d tests deselected by %r" %(
len(self.stats['deselected']), " ".join(l)), bold=True)
if l:
self.write_sep("=", "%d tests deselected by %r" %(
len(self.stats['deselected']), " ".join(l)), bold=True)
def repr_pythonversion(v=None):
if v is None:

View File

@@ -40,7 +40,7 @@ class TempdirHandler:
basetemp.mkdir()
else:
basetemp = py.path.local.make_numbered_dir(prefix='pytest-')
self._basetemp = t = basetemp
self._basetemp = t = basetemp.realpath()
self.trace("new basetemp", t)
return t
@@ -54,15 +54,15 @@ def pytest_configure(config):
mp.setattr(config, '_tmpdirhandler', t, raising=False)
mp.setattr(pytest, 'ensuretemp', t.ensuretemp, raising=False)
def pytest_funcarg__tmpdir(request):
@pytest.fixture
def tmpdir(request):
"""return a temporary directory path object
which is unique to each test function invocation,
created as a sub directory of the base temporary
directory. The returned object is a `py.path.local`_
path object.
"""
name = request._pyfuncitem.name
name = request.node.name
name = py.std.re.sub("[\W]", "_", name)
x = request.config._tmpdirhandler.mktemp(name, numbered=True)
return x

View File

@@ -2,6 +2,9 @@
import pytest, py
import sys, pdb
# for transfering markers
from _pytest.python import transfer_markers
def pytest_pycollect_makeitem(collector, name, obj):
unittest = sys.modules.get('unittest')
if unittest is None:
@@ -17,18 +20,42 @@ def pytest_pycollect_makeitem(collector, name, obj):
return UnitTestCase(name, parent=collector)
class UnitTestCase(pytest.Class):
nofuncargs = True # marker for fixturemanger.getfixtureinfo()
# to declare that our children do not support funcargs
def collect(self):
self.session._fixturemanager.parsefactories(self, unittest=True)
loader = py.std.unittest.TestLoader()
module = self.getparent(pytest.Module).obj
cls = self.obj
foundsomething = False
for name in loader.getTestCaseNames(self.obj):
x = getattr(self.obj, name)
funcobj = getattr(x, 'im_func', x)
transfer_markers(funcobj, cls, module)
if hasattr(funcobj, 'todo'):
pytest.mark.xfail(reason=str(funcobj.todo))(funcobj)
yield TestCaseFunction(name, parent=self)
foundsomething = True
if not foundsomething:
runtest = getattr(self.obj, 'runTest', None)
if runtest is not None:
ut = sys.modules.get("twisted.trial.unittest", None)
if ut is None or runtest != ut.TestCase.runTest:
yield TestCaseFunction('runTest', parent=self)
def setup(self):
if getattr(self.obj, '__unittest_skip__', False):
return
meth = getattr(self.obj, 'setUpClass', None)
if meth is not None:
meth()
super(UnitTestCase, self).setup()
def teardown(self):
if getattr(self.obj, '__unittest_skip__', False):
return
meth = getattr(self.obj, 'tearDownClass', None)
if meth is not None:
meth()
@@ -37,12 +64,6 @@ class UnitTestCase(pytest.Class):
class TestCaseFunction(pytest.Function):
_excinfo = None
def __init__(self, name, parent):
super(TestCaseFunction, self).__init__(name, parent)
if hasattr(self._obj, 'todo'):
getattr(self._obj, 'im_func', self._obj).xfail = \
pytest.mark.xfail(reason=str(self._obj.todo))
def setup(self):
self._testcase = self.parent.obj(self.name)
self._obj = getattr(self._testcase, self.name)
@@ -52,6 +73,8 @@ class TestCaseFunction(pytest.Function):
pytest.skip(self._obj.skip)
if hasattr(self._testcase, 'setup_method'):
self._testcase.setup_method(self._obj)
if hasattr(self, "_request"):
self._request._fillfixtures()
def teardown(self):
if hasattr(self._testcase, 'teardown_method'):
@@ -87,28 +110,37 @@ class TestCaseFunction(pytest.Function):
self._addexcinfo(rawexcinfo)
def addFailure(self, testcase, rawexcinfo):
self._addexcinfo(rawexcinfo)
def addSkip(self, testcase, reason):
try:
pytest.skip(reason)
except pytest.skip.Exception:
self._addexcinfo(sys.exc_info())
def addExpectedFailure(self, testcase, rawexcinfo, reason):
def addExpectedFailure(self, testcase, rawexcinfo, reason=""):
try:
pytest.xfail(str(reason))
except pytest.xfail.Exception:
self._addexcinfo(sys.exc_info())
def addUnexpectedSuccess(self, testcase, reason):
pass
def addUnexpectedSuccess(self, testcase, reason=""):
self._unexpectedsuccess = reason
def addSuccess(self, testcase):
pass
def stopTest(self, testcase):
pass
def runtest(self):
self._testcase(result=self)
def _prunetraceback(self, excinfo):
pytest.Function._prunetraceback(self, excinfo)
excinfo.traceback = excinfo.traceback.filter(lambda x:not x.frame.f_globals.get('__unittest'))
traceback = excinfo.traceback.filter(
lambda x:not x.frame.f_globals.get('__unittest'))
if traceback:
excinfo.traceback = traceback
@pytest.mark.tryfirst
def pytest_runtest_makereport(item, call):

View File

@@ -46,7 +46,7 @@ except ImportError:
args = [quote(arg) for arg in args]
return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
DEFAULT_VERSION = "0.6.19"
DEFAULT_VERSION = "0.6.27"
DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
SETUPTOOLS_FAKED_VERSION = "0.6c11"
@@ -63,7 +63,7 @@ Description: xxx
""" % SETUPTOOLS_FAKED_VERSION
def _install(tarball):
def _install(tarball, install_args=()):
# extracting the tarball
tmpdir = tempfile.mkdtemp()
log.warn('Extracting in %s', tmpdir)
@@ -81,7 +81,7 @@ def _install(tarball):
# installing
log.warn('Installing Distribute')
if not _python_cmd('setup.py', 'install'):
if not _python_cmd('setup.py', 'install', *install_args):
log.warn('Something went wrong during the installation.')
log.warn('See the error message above.')
finally:
@@ -306,6 +306,9 @@ def _create_fake_setuptools_pkg_info(placeholder):
log.warn('%s already exists', pkg_info)
return
if not os.access(pkg_info, os.W_OK):
log.warn("Don't have permissions to write %s, skipping", pkg_info)
log.warn('Creating %s', pkg_info)
f = open(pkg_info, 'w')
try:
@@ -474,11 +477,20 @@ def _extractall(self, path=".", members=None):
else:
self._dbg(1, "tarfile: %s" % e)
def _build_install_args(argv):
install_args = []
user_install = '--user' in argv
if user_install and sys.version_info < (2,6):
log.warn("--user requires Python 2.6 or later")
raise SystemExit(1)
if user_install:
install_args.append('--user')
return install_args
def main(argv, version=DEFAULT_VERSION):
"""Install or upgrade setuptools and EasyInstall"""
tarball = download_setuptools()
_install(tarball)
_install(tarball, _build_install_args(argv))
if __name__ == '__main__':

144
doc/en/Makefile Normal file
View File

@@ -0,0 +1,144 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
SITETARGET=latest
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
regen:
PYTHONDONTWRITEBYTECODE=1 COLUMNS=76 regendoc --update *.txt */*.txt
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
install: html
rsync -avz _build/html/ pytest.org:/www/pytest.org/$(SITETARGET)
installpdf: latexpdf
@scp $(BUILDDIR)/latex/pytest.pdf pytest.org:/www/pytest.org/$(SITETARGET)
installall: clean install installpdf
@echo "done"
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pytest.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pytest.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/pytest"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pytest"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
make -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."

View File

@@ -5,6 +5,14 @@ Release announcements
.. toctree::
:maxdepth: 2
release-2.3.4
release-2.3.3
release-2.3.2
release-2.3.1
release-2.3.0
release-2.2.4
release-2.2.2
release-2.2.1
release-2.2.0
release-2.1.3
release-2.1.2

View File

@@ -0,0 +1,67 @@
py.test 2.0.1: bug fixes
===========================================================================
Welcome to pytest-2.0.1, a maintenance and bug fix release of pytest,
a mature testing tool for Python, supporting CPython 2.4-3.2, Jython
and latest PyPy interpreters. See extensive docs with tested examples here:
http://pytest.org/
If you want to install or upgrade pytest, just type one of::
pip install -U pytest # or
easy_install -U pytest
Many thanks to all issue reporters and people asking questions or
complaining. Particular thanks to Floris Bruynooghe and Ronny Pfannschmidt
for their great coding contributions and many others for feedback and help.
best,
holger krekel
Changes between 2.0.0 and 2.0.1
----------------------------------------------
- refine and unify initial capturing so that it works nicely
even if the logging module is used on an early-loaded conftest.py
file or plugin.
- fix issue12 - show plugin versions with "--version" and
"--traceconfig" and also document how to add extra information
to reporting test header
- fix issue17 (import-* reporting issue on python3) by
requiring py>1.4.0 (1.4.1 is going to include it)
- fix issue10 (numpy arrays truth checking) by refining
assertion interpretation in py lib
- fix issue15: make nose compatibility tests compatible
with python3 (now that nose-1.0 supports python3)
- remove somewhat surprising "same-conftest" detection because
it ignores conftest.py when they appear in several subdirs.
- improve assertions ("not in"), thanks Floris Bruynooghe
- improve behaviour/warnings when running on top of "python -OO"
(assertions and docstrings are turned off, leading to potential
false positives)
- introduce a pytest_cmdline_processargs(args) hook
to allow dynamic computation of command line arguments.
This fixes a regression because py.test prior to 2.0
allowed to set command line options from conftest.py
files which so far pytest-2.0 only allowed from ini-files now.
- fix issue7: assert failures in doctest modules.
unexpected failures in doctests will not generally
show nicer, i.e. within the doctest failing context.
- fix issue9: setup/teardown functions for an xfail-marked
test will report as xfail if they fail but report as normally
passing (not xpassing) if they succeed. This only is true
for "direct" setup/teardown invocations because teardown_class/
teardown_module cannot closely relate to a single test.
- fix issue14: no logging errors at process exit
- refinements to "collecting" output on non-ttys
- refine internal plugin registration and --traceconfig output
- introduce a mechanism to prevent/unregister plugins from the
command line, see http://pytest.org/latest/plugins.html#cmdunregister
- activate resultlog plugin by default
- fix regression wrt yielded tests which due to the
collection-before-running semantics were not
setup as with pytest 1.3.4. Note, however, that
the recommended and much cleaner way to do test
parametrization remains the "pytest_generate_tests"
mechanism, see the docs.

View File

@@ -0,0 +1,43 @@
pytest-2.2.2: bug fixes
===========================================================================
pytest-2.2.2 (updated to 2.2.3 to fix packaging issues) is a minor
backward-compatible release of the versatile py.test testing tool. It
contains bug fixes and a few refinements particularly to reporting with
"--collectonly", see below for betails.
For general information see here:
http://pytest.org/
To install or upgrade pytest:
pip install -U pytest # or
easy_install -U pytest
Special thanks for helping on this release to Ronny Pfannschmidt
and Ralf Schmitt and the contributors of issues.
best,
holger krekel
Changes between 2.2.1 and 2.2.2
----------------------------------------
- fix issue101: wrong args to unittest.TestCase test function now
produce better output
- fix issue102: report more useful errors and hints for when a
test directory was renamed and some pyc/__pycache__ remain
- fix issue106: allow parametrize to be applied multiple times
e.g. from module, class and at function level.
- fix issue107: actually perform session scope finalization
- don't check in parametrize if indirect parameters are funcarg names
- add chdir method to monkeypatch funcarg
- fix crash resulting from calling monkeypatch undo a second time
- fix issue115: make --collectonly robust against early failure
(missing files/directories)
- "-qq --collectonly" now shows only files and the number of tests in them
- "-q --collectonly" now shows test ids
- allow adding of attributes to test reports such that it also works
with distributed testing (no upgrade of pytest-xdist needed)

View File

@@ -0,0 +1,39 @@
pytest-2.2.4: bug fixes, better junitxml/unittest/python3 compat
===========================================================================
pytest-2.2.4 is a minor backward-compatible release of the versatile
py.test testing tool. It contains bug fixes and a few refinements
to junitxml reporting, better unittest- and python3 compatibility.
For general information see here:
http://pytest.org/
To install or upgrade pytest:
pip install -U pytest # or
easy_install -U pytest
Special thanks for helping on this release to Ronny Pfannschmidt
and Benjamin Peterson and the contributors of issues.
best,
holger krekel
Changes between 2.2.3 and 2.2.4
-----------------------------------
- fix error message for rewritten assertions involving the % operator
- fix issue 126: correctly match all invalid xml characters for junitxml
binary escape
- fix issue with unittest: now @unittest.expectedFailure markers should
be processed correctly (you can also use @pytest.mark markers)
- document integration with the extended distribute/setuptools test commands
- fix issue 140: propperly get the real functions
of bound classmethods for setup/teardown_class
- fix issue #141: switch from the deceased paste.pocoo.org to bpaste.net
- fix issue #143: call unconfigure/sessionfinish always when
configure/sessionstart where called
- fix issue #144: better mangle test ids to junitxml classnames
- upgrade distribute_setup.py to 0.6.27

View File

@@ -0,0 +1,134 @@
pytest-2.3: improved fixtures / better unittest integration
=============================================================================
pytest-2.3 comes with many major improvements for fixture/funcarg management
and parametrized testing in Python. It is now easier, more efficient and
more predicatable to re-run the same tests with different fixture
instances. Also, you can directly declare the caching "scope" of
fixtures so that dependent tests throughout your whole test suite can
re-use database or other expensive fixture objects with ease. Lastly,
it's possible for fixture functions (formerly known as funcarg
factories) to use other fixtures, allowing for a completely modular and
re-useable fixture design.
For detailed info and tutorial-style examples, see:
http://pytest.org/latest/fixture.html
Moreover, there is now support for using pytest fixtures/funcargs with
unittest-style suites, see here for examples:
http://pytest.org/latest/unittest.html
Besides, more unittest-test suites are now expected to "simply work"
with pytest.
All changes are backward compatible and you should be able to continue
to run your test suites and 3rd party plugins that worked with
pytest-2.2.4.
If you are interested in the precise reasoning (including examples) of the
pytest-2.3 fixture evolution, please consult
http://pytest.org/latest/funcarg_compare.html
For general info on installation and getting started:
http://pytest.org/latest/getting-started.html
Docs and PDF access as usual at:
http://pytest.org
and more details for those already in the knowing of pytest can be found
in the CHANGELOG below.
Particular thanks for this release go to Floris Bruynooghe, Alex Okrushko
Carl Meyer, Ronny Pfannschmidt, Benjamin Peterson and Alex Gaynor for helping
to get the new features right and well integrated. Ronny and Floris
also helped to fix a number of bugs and yet more people helped by
providing bug reports.
have fun,
holger krekel
Changes between 2.2.4 and 2.3.0
-----------------------------------
- fix issue202 - better automatic names for parametrized test functions
- fix issue139 - introduce @pytest.fixture which allows direct scoping
and parametrization of funcarg factories. Introduce new @pytest.setup
marker to allow the writing of setup functions which accept funcargs.
- fix issue198 - conftest fixtures were not found on windows32 in some
circumstances with nested directory structures due to path manipulation issues
- fix issue193 skip test functions with were parametrized with empty
parameter sets
- fix python3.3 compat, mostly reporting bits that previously depended
on dict ordering
- introduce re-ordering of tests by resource and parametrization setup
which takes precedence to the usual file-ordering
- fix issue185 monkeypatching time.time does not cause pytest to fail
- fix issue172 duplicate call of pytest.setup-decoratored setup_module
functions
- fix junitxml=path construction so that if tests change the
current working directory and the path is a relative path
it is constructed correctly from the original current working dir.
- fix "python setup.py test" example to cause a proper "errno" return
- fix issue165 - fix broken doc links and mention stackoverflow for FAQ
- catch unicode-issues when writing failure representations
to terminal to prevent the whole session from crashing
- fix xfail/skip confusion: a skip-mark or an imperative pytest.skip
will now take precedence before xfail-markers because we
can't determine xfail/xpass status in case of a skip. see also:
http://stackoverflow.com/questions/11105828/in-py-test-when-i-explicitly-skip-a-test-that-is-marked-as-xfail-how-can-i-get
- always report installed 3rd party plugins in the header of a test run
- fix issue160: a failing setup of an xfail-marked tests should
be reported as xfail (not xpass)
- fix issue128: show captured output when capsys/capfd are used
- fix issue179: propperly show the dependency chain of factories
- pluginmanager.register(...) now raises ValueError if the
plugin has been already registered or the name is taken
- fix issue159: improve http://pytest.org/latest/faq.html
especially with respect to the "magic" history, also mention
pytest-django, trial and unittest integration.
- make request.keywords and node.keywords writable. All descendant
collection nodes will see keyword values. Keywords are dictionaries
containing markers and other info.
- fix issue 178: xml binary escapes are now wrapped in py.xml.raw
- fix issue 176: correctly catch the builtin AssertionError
even when we replaced AssertionError with a subclass on the
python level
- factory discovery no longer fails with magic global callables
that provide no sane __code__ object (mock.call for example)
- fix issue 182: testdir.inprocess_run now considers passed plugins
- fix issue 188: ensure sys.exc_info is clear on python2
before calling into a test
- fix issue 191: add unittest TestCase runTest method support
- fix issue 156: monkeypatch correctly handles class level descriptors
- reporting refinements:
- pytest_report_header now receives a "startdir" so that
you can use startdir.bestrelpath(yourpath) to show
nice relative path
- allow plugins to implement both pytest_report_header and
pytest_sessionstart (sessionstart is invoked first).
- don't show deselected reason line if there is none
- py.test -vv will show all of assert comparisations instead of truncating

View File

@@ -0,0 +1,39 @@
pytest-2.3.1: fix regression with factory functions
===========================================================================
pytest-2.3.1 is a quick follow-up release:
- fix issue202 - regression with fixture functions/funcarg factories:
using "self" is now safe again and works as in 2.2.4. Thanks
to Eduard Schettino for the quick bug report.
- disable pexpect pytest self tests on Freebsd - thanks Koob for the
quick reporting
- fix/improve interactive docs with --markers
See
http://pytest.org/
for general information. To install or upgrade pytest:
pip install -U pytest # or
easy_install -U pytest
best,
holger krekel
Changes between 2.3.0 and 2.3.1
-----------------------------------
- fix issue202 - fix regression: using "self" from fixture functions now
works as expected (it's the same "self" instance that a test method
which uses the fixture sees)
- skip pexpect using tests (test_pdb.py mostly) on freebsd* systems
due to pexpect not supporting it properly (hanging)
- link to web pages from --markers output which provides help for
pytest.mark.* usage.

View File

@@ -0,0 +1,57 @@
pytest-2.3.2: some fixes and more traceback-printing speed
===========================================================================
pytest-2.3.2 is a another stabilization release:
- issue 205: fixes a regression with conftest detection
- issue 208/29: fixes traceback-printing speed in some bad cases
- fix teardown-ordering for parametrized setups
- fix unittest and trial compat behaviour with respect to runTest() methods
- issue 206 and others: some improvements to packaging
- fix issue127 and others: improve some docs
See
http://pytest.org/
for general information. To install or upgrade pytest:
pip install -U pytest # or
easy_install -U pytest
best,
holger krekel
Changes between 2.3.1 and 2.3.2
-----------------------------------
- fix issue208 and fix issue29 use new py version to avoid long pauses
when printing tracebacks in long modules
- fix issue205 - conftests in subdirs customizing
pytest_pycollect_makemodule and pytest_pycollect_makeitem
now work properly
- fix teardown-ordering for parametrized setups
- fix issue127 - better documentation for pytest_addoption
and related objects.
- fix unittest behaviour: TestCase.runtest only called if there are
test methods defined
- improve trial support: don't collect its empty
unittest.TestCase.runTest() method
- "python setup.py test" now works with pytest itself
- fix/improve internal/packaging related bits:
- exception message check of test_nose.py now passes on python33 as well
- issue206 - fix test_assertrewrite.py to work when a global
PYTHONDONTWRITEBYTECODE=1 is present
- add tox.ini to pytest distribution so that ignore-dirs and others config
bits are properly distributed for maintainers who run pytest-own tests

View File

@@ -0,0 +1,62 @@
pytest-2.3.3: integration fixes, py24 suport, ``*/**`` shown in traceback
===========================================================================
pytest-2.3.3 is a another stabilization release of the py.test tool
which offers uebersimple assertions, scalable fixture mechanisms
and deep customization for testing with Python. Particularly,
this release provides:
- integration fixes and improvements related to flask, numpy, nose,
unittest, mock
- makes pytest work on py24 again (yes, people sometimes still need to use it)
- show ``*,**`` args in pytest tracebacks
Thanks to Manuel Jacob, Thomas Waldmann, Ronny Pfannschmidt, Pavel Repin
and Andreas Taumoefolau for providing patches and all for the issues.
See
http://pytest.org/
for general information. To install or upgrade pytest:
pip install -U pytest # or
easy_install -U pytest
best,
holger krekel
Changes between 2.3.2 and 2.3.3
-----------------------------------
- fix issue214 - parse modules that contain special objects like e. g.
flask's request object which blows up on getattr access if no request
is active. thanks Thomas Waldmann.
- fix issue213 - allow to parametrize with values like numpy arrays that
do not support an __eq__ operator
- fix issue215 - split test_python.org into multiple files
- fix issue148 - @unittest.skip on classes is now recognized and avoids
calling setUpClass/tearDownClass, thanks Pavel Repin
- fix issue209 - reintroduce python2.4 support by depending on newer
pylib which re-introduced statement-finding for pre-AST interpreters
- nose support: only call setup if its a callable, thanks Andrew
Taumoefolau
- fix issue219 - add py2.4-3.3 classifiers to TROVE list
- in tracebacks *,** arg values are now shown next to normal arguments
(thanks Manuel Jacob)
- fix issue217 - support mock.patch with pytest's fixtures - note that
you need either mock-1.0.1 or the python3.3 builtin unittest.mock.
- fix issue127 - improve documentation for pytest_addoption() and
add a ``config.getoption(name)`` helper function for consistency.

View File

@@ -0,0 +1,39 @@
pytest-2.3.4: stabilization, more flexible selection via "-k expr"
===========================================================================
pytest-2.3.4 is a small stabilization release of the py.test tool
which offers uebersimple assertions, scalable fixture mechanisms
and deep customization for testing with Python. This release
comes with the following fixes and features:
- make "-k" option accept an expressions the same as with "-m" so that one
can write: -k "name1 or name2" etc. This is a slight usage incompatibility
if you used special syntax like "TestClass.test_method" which you now
need to write as -k "TestClass and test_method" to match a certain
method in a certain test class.
- allow to dynamically define markers via
item.keywords[...]=assignment integrating with "-m" option
- yielded test functions will now have autouse-fixtures active but
cannot accept fixtures as funcargs - it's anyway recommended to
rather use the post-2.0 parametrize features instead of yield, see:
http://pytest.org/latest/example/parametrize.html
- fix autouse-issue where autouse-fixtures would not be discovered
if defined in a a/conftest.py file and tests in a/tests/test_some.py
- fix issue226 - LIFO ordering for fixture teardowns
- fix issue224 - invocations with >256 char arguments now work
- fix issue91 - add/discuss package/directory level setups in example
- fixes related to autouse discovery and calling
Thanks in particular to Thomas Waldmann for spotting and reporting issues.
See
http://pytest.org/
for general information. To install or upgrade pytest:
pip install -U pytest # or
easy_install -U pytest
best,
holger krekel

View File

@@ -10,14 +10,15 @@ py.test reference documentation
builtin.txt
customize.txt
assert.txt
funcargs.txt
fixture.txt
parametrize.txt
xunit_setup.txt
capture.txt
monkeypatch.txt
xdist.txt
tmpdir.txt
skipping.txt
mark.txt
skipping.txt
recwarn.txt
unittest.txt
nose.txt

View File

@@ -2,7 +2,10 @@
The writing and reporting of assertions in tests
==================================================
.. _`assertfeedback`:
.. _`assert with the assert statement`:
.. _`assert`:
Asserting with the ``assert`` statement
---------------------------------------------------------
@@ -23,8 +26,8 @@ you will see the return value of the function call::
$ py.test test_assert1.py
=========================== test session starts ============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1
collecting ... collected 1 items
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 1 items
test_assert1.py F
@@ -37,7 +40,7 @@ you will see the return value of the function call::
E + where 3 = f()
test_assert1.py:5: AssertionError
========================= 1 failed in 0.02 seconds =========================
========================= 1 failed in 0.01 seconds =========================
py.test has support for showing the values of the most common subexpressions
including calls, attributes, comparisons, and binary and unary
@@ -54,6 +57,8 @@ will be simply shown in the traceback.
See :ref:`assert-details` for more information on assertion introspection.
.. _`assertraises`:
Assertions about expected exceptions
------------------------------------------
@@ -105,8 +110,8 @@ if you run this module::
$ py.test test_assert2.py
=========================== test session starts ============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1
collecting ... collected 1 items
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 1 items
test_assert2.py F
@@ -124,7 +129,7 @@ if you run this module::
E '5'
test_assert2.py:5: AssertionError
========================= 1 failed in 0.02 seconds =========================
========================= 1 failed in 0.01 seconds =========================
Special comparisons are done for a number of cases:
@@ -168,7 +173,6 @@ you can run the test module and get the custom output defined in
the conftest file::
$ py.test -q test_foocompare.py
collecting ... collected 1 items
F
================================= FAILURES =================================
_______________________________ test_compare _______________________________
@@ -181,7 +185,6 @@ the conftest file::
E vals: 1 != 2
test_foocompare.py:8: AssertionError
1 failed in 0.02 seconds
.. _assert-details:
.. _`assert introspection`:

186
doc/en/attic_fixtures.txt Normal file
View File

@@ -0,0 +1,186 @@
**Test classes, modules or whole projects can make use of
one or more fixtures**. All required fixture functions will execute
before a test from the specifying context executes. As You can use this
to make tests operate from a pre-initialized directory or with
certain environment variables or with pre-configured global application
settings.
For example, the Django_ project requires database
initialization to be able to import from and use its model objects.
For that, the `pytest-django`_ plugin provides fixtures which your
project can then easily depend or extend on, simply by referencing the
name of the particular fixture.
Fixture functions have limited visilibity which depends on where they
are defined. If they are defined on a test class, only its test methods
may use it. A fixture defined in a module can only be used
from that test module. A fixture defined in a conftest.py file
can only be used by the tests below the directory of that file.
Lastly, plugins can define fixtures which are available across all
projects.
Python, Java and many other languages support a so called xUnit_ style
for providing a fixed state, `test fixtures`_, for running tests. It
typically involves calling a autouse function ahead and a teardown
function after test execute. In 2005 pytest introduced a scope-specific
model of automatically detecting and calling autouse and teardown
functions on a per-module, class or function basis. The Python unittest
package and nose have subsequently incorporated them. This model
remains supported by pytest as :ref:`classic xunit`.
One property of xunit fixture functions is that they work implicitely
by preparing global state or setting attributes on TestCase objects.
By contrast, pytest provides :ref:`funcargs` which allow to
dependency-inject application test state into test functions or
methods as function arguments. If your application is sufficiently modular
or if you are creating a new project, we recommend you now rather head over to
:ref:`funcargs` instead because many pytest users agree that using this
paradigm leads to better application and test organisation.
However, not all programs and frameworks work and can be tested in
a fully modular way. They rather require preparation of global state
like database autouse on which further fixtures like preparing application
specific tables or wrapping tests in transactions can take place. For those
needs, pytest-2.3 now supports new **fixture functions** which come with
a ton of improvements over classic xunit fixture writing. Fixture functions:
- allow to separate different autouse concerns into multiple modular functions
- can receive and fully interoperate with :ref:`funcargs <resources>`,
- are called multiple times if its funcargs are parametrized,
- don't need to be defined directly in your test classes or modules,
they can also be defined in a plugin or :ref:`conftest.py <conftest.py>` files and get called
- are called on a per-session, per-module, per-class or per-function basis
by means of a simple "scope" declaration.
- can access the :ref:`request <request>` object which allows to
introspect and interact with the (scoped) testcontext.
- can add cleanup functions which will be invoked when the last test
of the fixture test context has finished executing.
All of these features are now demonstrated by little examples.
test modules accessing a global resource
-------------------------------------------------------
.. note::
Relying on `global state is considered bad programming practise <http://en.wikipedia.org/wiki/Global_variable>`_ but when you work with an application
that relies on it you often have no choice.
If you want test modules to access a global resource,
you can stick the resource to the module globals in
a per-module autouse function. We use a :ref:`resource factory
<@pytest.fixture>` to create our global resource::
# content of conftest.py
import pytest
class GlobalResource:
def __init__(self):
pass
@pytest.fixture(scope="session")
def globresource():
return GlobalResource()
@pytest.fixture(scope="module")
def setresource(request, globresource):
request.module.globresource = globresource
Now any test module can access ``globresource`` as a module global::
# content of test_glob.py
def test_1():
print ("test_1 %s" % globresource)
def test_2():
print ("test_2 %s" % globresource)
Let's run this module without output-capturing::
$ py.test -qs test_glob.py
FF
================================= FAILURES =================================
__________________________________ test_1 __________________________________
def test_1():
> print ("test_1 %s" % globresource)
E NameError: global name 'globresource' is not defined
test_glob.py:3: NameError
__________________________________ test_2 __________________________________
def test_2():
> print ("test_2 %s" % globresource)
E NameError: global name 'globresource' is not defined
test_glob.py:5: NameError
The two tests see the same global ``globresource`` object.
Parametrizing the global resource
+++++++++++++++++++++++++++++++++++++++++++++++++
We extend the previous example and add parametrization to the globresource
factory and also add a finalizer::
# content of conftest.py
import pytest
class GlobalResource:
def __init__(self, param):
self.param = param
@pytest.fixture(scope="session", params=[1,2])
def globresource(request):
g = GlobalResource(request.param)
def fin():
print "finalizing", g
request.addfinalizer(fin)
return g
@pytest.fixture(scope="module")
def setresource(request, globresource):
request.module.globresource = globresource
And then re-run our test module::
$ py.test -qs test_glob.py
FF
================================= FAILURES =================================
__________________________________ test_1 __________________________________
def test_1():
> print ("test_1 %s" % globresource)
E NameError: global name 'globresource' is not defined
test_glob.py:3: NameError
__________________________________ test_2 __________________________________
def test_2():
> print ("test_2 %s" % globresource)
E NameError: global name 'globresource' is not defined
test_glob.py:5: NameError
We are now running the two tests twice with two different global resource
instances. Note that the tests are ordered such that only
one instance is active at any given time: the finalizer of
the first globresource instance is called before the second
instance is created and sent to the autouse functions.

View File

@@ -1,37 +1,78 @@
.. _`pytest helpers`:
Pytest builtin helpers
Pytest API and builtin fixtures
================================================
builtin pytest.* functions and helping objects
-----------------------------------------------------
This is a list of ``pytest.*`` API functions and fixtures.
You can always use an interactive Python prompt and type::
For information on plugin hooks and objects, see :ref:`plugins`.
For information on the ``pytest.mark`` mechanism, see :ref:`mark`.
For the below objects, you can also interactively ask for help, e.g. by
typing on the Python interactive prompt something like::
import pytest
help(pytest)
to get an overview on the globally available helpers.
.. currentmodule:: pytest
.. automodule:: pytest
Invoking pytest interactively
---------------------------------------------------
.. autofunction:: main
More examples at :ref:`pytest.main-usage`
Helpers for assertions about Exceptions/Warnings
--------------------------------------------------------
.. autofunction:: raises
Examples at :ref:`assertraises`.
.. autofunction:: deprecated_call
Raising a specific test outcome
--------------------------------------
You can use the following functions in your test, fixture or setup
functions to force a certain test outcome. Note that most often
you can rather use declarative marks, see :ref:`skipping`.
.. autofunction:: _pytest.runner.fail
.. autofunction:: _pytest.runner.skip
.. autofunction:: _pytest.runner.importorskip
.. autofunction:: _pytest.skipping.xfail
.. autofunction:: _pytest.runner.exit
fixtures and requests
-----------------------------------------------------
To mark a fixture function:
.. autofunction:: _pytest.python.fixture
Tutorial at :ref:`fixtures`.
The ``request`` object that can be used from fixture functions.
.. autoclass:: _pytest.python.FixtureRequest()
:members:
.. _builtinfixtures:
.. _builtinfuncargs:
Builtin function arguments
-----------------------------------------------------
Builtin fixtures/function arguments
-----------------------------------------
You can ask for available builtin or project-custom
:ref:`function arguments <funcargs>` by typing::
:ref:`fixtures <fixtures>` by typing::
$ py.test --funcargs
=========================== test session starts ============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1
collected 0 items
pytestconfig
the pytest config object with access to command line opts.
$ py.test -q --fixtures
capsys
enables capturing of writes to sys.stdout/sys.stderr and makes
captured output available via ``capsys.readouterr()`` method calls
@@ -42,13 +83,6 @@ You can ask for available builtin or project-custom
captured output available via ``capsys.readouterr()`` method calls
which return a ``(out, err)`` tuple.
tmpdir
return a temporary directory path object
which is unique to each test function invocation,
created as a sub directory of the base temporary
directory. The returned object is a `py.path.local`_
path object.
monkeypatch
The returned ``monkeypatch`` funcarg provides these
helper methods to modify objects, dictionaries or os.environ::
@@ -60,12 +94,15 @@ You can ask for available builtin or project-custom
monkeypatch.setenv(name, value, prepend=False)
monkeypatch.delenv(name, value, raising=True)
monkeypatch.syspath_prepend(path)
monkeypatch.chdir(path)
All modifications will be undone after the requesting
test function has finished. The ``raising``
parameter determines if a KeyError or AttributeError
will be raised if the set/deletion operation has no target.
pytestconfig
the pytest config object with access to command line opts.
recwarn
Return a WarningsRecorder instance that provides these methods:
@@ -75,5 +112,11 @@ You can ask for available builtin or project-custom
See http://docs.python.org/library/warnings.html for information
on warning categories.
tmpdir
return a temporary directory path object
which is unique to each test function invocation,
created as a sub directory of the base temporary
directory. The returned object is a `py.path.local`_
path object.
============================= in 0.00 seconds =============================

View File

@@ -64,8 +64,8 @@ of the failing function and hide the other one::
$ py.test
=========================== test session starts ============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1
collecting ... collected 2 items
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 2 items
test_module.py .F
@@ -78,8 +78,8 @@ of the failing function and hide the other one::
test_module.py:9: AssertionError
----------------------------- Captured stdout ------------------------------
setting up <function test_func2 at 0x101314c80>
==================== 1 failed, 1 passed in 0.03 seconds ====================
setting up <function test_func2 at 0x1e12f50>
==================== 1 failed, 1 passed in 0.01 seconds ====================
Accessing captured output from a test function
---------------------------------------------------

View File

@@ -4,4 +4,4 @@
Changelog history
=================================
.. include:: ../CHANGELOG
.. include:: ../../CHANGELOG

287
doc/en/conf.py Normal file
View File

@@ -0,0 +1,287 @@
# -*- coding: utf-8 -*-
#
# pytest documentation build configuration file, created by
# sphinx-quickstart on Fri Oct 8 17:54:28 2010.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The full version, including alpha/beta/rc tags.
# The short X.Y version.
version = release = "2.3.4"
import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
autodoc_member_order = "bysource"
todo_include_todos = 1
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.autosummary',
'sphinx.ext.intersphinx', 'sphinx.ext.viewcode']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.txt'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'contents'
# General information about the project.
project = u'pytest'
copyright = u'2012, holger krekel'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['links.inc', '_build', 'naming20.txt', 'test/*',
"old_*",
'*attic*',
'*/attic*',
'funcargs.txt',
'setup.txt',
'example/remoteinterp.txt',
]
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
add_module_names = False
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'sphinxdoc'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
html_short_title = "pytest-%s" % release
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
#html_sidebars = {'index': 'indexsidebar.html'}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
#html_additional_pages = {'index': 'index.html'}
# If false, no module index is generated.
html_domain_indices = True
# If false, no index is generated.
html_use_index = False
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
html_show_sourcelink = False
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'pytestdoc'
# -- Options for LaTeX output --------------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('contents', 'pytest.tex', u'pytest Documentation',
u'holger krekel, http://merlinux.eu', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
latex_domain_indices = False
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('usage', 'pytest', u'pytest usage',
[u'holger krekel at merlinux eu'], 1)
]
# -- Options for Epub output ---------------------------------------------------
# Bibliographic Dublin Core info.
epub_title = u'pytest'
epub_author = u'holger krekel at merlinux eu'
epub_publisher = u'holger krekel at merlinux eu'
epub_copyright = u'2012, holger krekel et alii'
# The language of the text. It defaults to the language option
# or en if the language is not set.
#epub_language = ''
# The scheme of the identifier. Typical schemes are ISBN or URL.
#epub_scheme = ''
# The unique identifier of the text. This can be a ISBN number
# or the project homepage.
#epub_identifier = ''
# A unique identification for the text.
#epub_uid = ''
# HTML files that should be inserted before the pages created by sphinx.
# The format is a list of tuples containing the path and title.
#epub_pre_files = []
# HTML files shat should be inserted after the pages created by sphinx.
# The format is a list of tuples containing the path and title.
#epub_post_files = []
# A list of files that should not be packed into the epub file.
#epub_exclude_files = []
# The depth of the table of contents in toc.ncx.
#epub_tocdepth = 3
# Allow duplicate toc entries.
#epub_tocdup = True
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'python': ('http://docs.python.org/', None),
# 'lib': ("http://docs.python.org/2.7library/", None),
}
def setup(app):
#from sphinx.ext.autodoc import cut_lines
#app.connect('autodoc-process-docstring', cut_lines(4, what=['module']))
app.add_description_unit('confval', 'confval',
objname='configuration value',
indextemplate='pair: %s; configuration value')

View File

@@ -9,6 +9,10 @@ Contact channels
2.0 and above). You may also peek at the `old issue tracker`_ but please
don't submit bugs there anymore.
- `pytest on stackoverflow.com <http://stackoverflow.com/search?q=pytest>`_
to post questions with the tag ``pytest``. New Questions will usually
be seen by pytest users or developers.
- `Testing In Python`_: a mailing list for Python testing tools and discussion.
- `py-dev developers list`_ pytest specific announcements and discussions.

View File

@@ -1,10 +1,10 @@
.. _toc:
Full pytest documenation
========================
Full pytest documentation
===========================
`Download latest version as PDF <http://media.readthedocs.org/pdf/pytest/latest/pytest.pdf>`_
`Download latest version as PDF <pytest.pdf>`_
.. `Download latest version as EPUB <http://media.readthedocs.org/epub/pytest/latest/pytest.epub>`_
@@ -12,11 +12,12 @@ Full pytest documenation
:maxdepth: 2
overview
example/index
apiref
plugins
example/index
talks
develop
funcarg_compare.txt
announce/index
.. toctree::

View File

@@ -12,6 +12,8 @@ configurations files by using the general help option::
This will display command line and configuration file settings
which were registered by installed plugins.
.. _inifiles:
How test configuration is read from configuration INI-files
-------------------------------------------------------------
@@ -23,8 +25,9 @@ It looks for file basenames in this order::
tox.ini
setup.cfg
Searching stops when the first ``[pytest]`` section is found.
There is no merging of configuration values from multiple files. Example::
Searching stops when the first ``[pytest]`` section is found in any of
these files. There is no merging of configuration values from multiple
files. Example::
py.test path/to/testdir
@@ -94,7 +97,7 @@ Builtin configuration file options
[seq] matches any character in seq
[!seq] matches any char not in seq
Default patterns are ``.* _* CVS {args}``. Setting a ``norecurse``
Default patterns are ``.* _* CVS {args}``. Setting a ``norecursedir``
replaces the default. Here is an example of how to avoid
certain directories::

View File

@@ -44,10 +44,9 @@ then you can just invoke ``py.test`` without command line options::
$ py.test
=========================== test session starts ============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1
collecting ... collected 1 items
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 1 items
mymodule.py .
========================= 1 passed in 0.05 seconds =========================
[?1034h
========================= 1 passed in 0.02 seconds =========================

View File

@@ -15,7 +15,7 @@ def test_generative(param1, param2):
assert param1 * 2 < param2
def pytest_generate_tests(metafunc):
if 'param1' in metafunc.funcargnames:
if 'param1' in metafunc.fixturenames:
metafunc.addcall(funcargs=dict(param1=3, param2=6))
class TestFailing(object):

View File

@@ -13,9 +13,9 @@ example: specifying and selecting acceptance tests
help="run (slow) acceptance tests")
def pytest_funcarg__accept(request):
return AcceptFuncarg(request)
return AcceptFixture(request)
class AcceptFuncarg:
class AcceptFixture:
def __init__(self, request):
if not request.config.option.acceptance:
pytest.skip("specify -A to run acceptance tests")
@@ -39,7 +39,7 @@ and the actual test function example:
If you run this test without specifying a command line option
the test will get skipped with an appropriate message. Otherwise
you can start to add convenience and test support methods
to your AcceptFuncarg and drive running of tools or
to your AcceptFixture and drive running of tools or
applications and provide ways to do assertions about
the output.

View File

@@ -0,0 +1,18 @@
import pytest
@pytest.fixture("session")
def setup(request):
setup = CostlySetup()
request.addfinalizer(setup.finalize)
return setup
class CostlySetup:
def __init__(self):
import time
print ("performing costly setup")
time.sleep(5)
self.timecostly = 1
def finalize(self):
del self.timecostly

33
doc/en/example/index.txt Normal file
View File

@@ -0,0 +1,33 @@
.. _examples:
Usages and Examples
===========================================
Here is a (growing) list of examples. :ref:`Contact <contact>` us if you
need more examples or have questions. Also take a look at the
:ref:`comprehensive documentation <toc>` which contains many example
snippets as well. Also, `pytest on stackoverflow.com
<http://stackoverflow.com/search?q=pytest>`_ often comes with example
answers.
For basic examples, see
- :doc:`../getting-started` for basic introductory examples
- :ref:`assert` for basic assertion examples
- :ref:`fixtures` for basic fixture/setup examples
- :ref:`parametrize` for basic test function parametrization
- :doc:`../unittest` for basic unittest integration
- :doc:`../nose` for basic nosetests integration
The following examples aim at various use cases you might encounter.
.. toctree::
:maxdepth: 2
reportingdemo.txt
simple.txt
parametrize.txt
markers.txt
pythoncollection.txt
nonpython.txt

467
doc/en/example/markers.txt Normal file
View File

@@ -0,0 +1,467 @@
.. _`mark examples`:
Working with custom markers
=================================================
Here are some example using the :ref:`mark` mechanism.
Marking test functions and selecting them for a run
----------------------------------------------------
You can "mark" a test function with custom metadata like this::
# content of test_server.py
import pytest
@pytest.mark.webtest
def test_send_http():
pass # perform some webtest test for your app
def test_something_quick():
pass
def test_another():
pass
.. versionadded:: 2.2
You can then restrict a test run to only run tests marked with ``webtest``::
$ py.test -v -m webtest
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 3 items
test_server.py:3: test_send_http PASSED
=================== 2 tests deselected by "-m 'webtest'" ===================
================== 1 passed, 2 deselected in 0.01 seconds ==================
Or the inverse, running all tests except the webtest ones::
$ py.test -v -m "not webtest"
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 3 items
test_server.py:6: test_something_quick PASSED
test_server.py:8: test_another PASSED
================= 1 tests deselected by "-m 'not webtest'" =================
================== 2 passed, 1 deselected in 0.01 seconds ==================
Using ``-k expr`` to select tests based on their name
-------------------------------------------------------
.. versionadded: 2.0/2.3.4
You can use the ``-k`` command line option to specify an expression
which implements a substring match on the test names instead of the
exact match on markers that ``-m`` provides. This makes it easy to
select tests based on their names::
$ py.test -v -k http # running with the above defined example module
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 3 items
test_server.py:3: test_send_http PASSED
====================== 2 tests deselected by '-khttp' ======================
================== 1 passed, 2 deselected in 0.01 seconds ==================
And you can also run all tests except the ones that match the keyword::
$ py.test -k "not send_http" -v
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 3 items
test_server.py:6: test_something_quick PASSED
test_server.py:8: test_another PASSED
================= 1 tests deselected by '-knot send_http' ==================
================== 2 passed, 1 deselected in 0.01 seconds ==================
Or to select "http" and "quick" tests::
$ py.test -k "http or quick" -v
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 3 items
test_server.py:3: test_send_http PASSED
test_server.py:6: test_something_quick PASSED
================= 1 tests deselected by '-khttp or quick' ==================
================== 2 passed, 1 deselected in 0.01 seconds ==================
Registering markers
-------------------------------------
.. versionadded:: 2.2
.. ini-syntax for custom markers:
Registering markers for your test suite is simple::
# content of pytest.ini
[pytest]
markers =
webtest: mark a test as a webtest.
You can ask which markers exist for your test suite - the list includes our just defined ``webtest`` markers::
$ py.test --markers
@pytest.mark.webtest: mark a test as a webtest.
@pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see http://pytest.org/latest/skipping.html
@pytest.mark.xfail(condition, reason=None, run=True): mark the the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. See http://pytest.org/latest/skipping.html
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in multiple different argument value sets. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2. see http://pytest.org/latest/parametrize.html for more info and examples.
@pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures
@pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
@pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
For an example on how to add and work with markers from a plugin, see
:ref:`adding a custom marker from a plugin`.
.. note::
It is recommended to explicitely register markers so that:
* there is one place in your test suite defining your markers
* asking for existing markers via ``py.test --markers`` gives good output
* typos in function markers are treated as an error if you use
the ``--strict`` option. Later versions of py.test are probably
going to treat non-registered markers as an error.
.. _`scoped-marking`:
Marking whole classes or modules
----------------------------------------------------
If you are programming with Python 2.6 or later you may use ``pytest.mark``
decorators with classes to apply markers to all of its test methods::
# content of test_mark_classlevel.py
import pytest
@pytest.mark.webtest
class TestClass:
def test_startup(self):
pass
def test_startup_and_more(self):
pass
This is equivalent to directly applying the decorator to the
two test functions.
To remain backward-compatible with Python 2.4 you can also set a
``pytestmark`` attribute on a TestClass like this::
import pytest
class TestClass:
pytestmark = pytest.mark.webtest
or if you need to use multiple markers you can use a list::
import pytest
class TestClass:
pytestmark = [pytest.mark.webtest, pytest.mark.slowtest]
You can also set a module level marker::
import pytest
pytestmark = pytest.mark.webtest
in which case it will be applied to all functions and
methods defined in the module.
.. _`adding a custom marker from a plugin`:
Custom marker and command line option to control test runs
----------------------------------------------------------
.. regendoc:wipe
Plugins can provide custom markers and implement specific behaviour
based on it. This is a self-contained example which adds a command
line option and a parametrized test function marker to run tests
specifies via named environments::
# content of conftest.py
import pytest
def pytest_addoption(parser):
parser.addoption("-E", action="store", metavar="NAME",
help="only run tests matching the environment NAME.")
def pytest_configure(config):
# register an additional marker
config.addinivalue_line("markers",
"env(name): mark test to run only on named environment")
def pytest_runtest_setup(item):
envmarker = item.keywords.get("env", None)
if envmarker is not None:
envname = envmarker.args[0]
if envname != item.config.getoption("-E"):
pytest.skip("test requires env %r" % envname)
A test file using this local plugin::
# content of test_someenv.py
import pytest
@pytest.mark.env("stage1")
def test_basic_db_operation():
pass
and an example invocations specifying a different environment than what
the test needs::
$ py.test -E stage2
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 1 items
test_someenv.py s
======================== 1 skipped in 0.01 seconds =========================
and here is one that specifies exactly the environment needed::
$ py.test -E stage1
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 1 items
test_someenv.py .
========================= 1 passed in 0.01 seconds =========================
The ``--markers`` option always gives you a list of available markers::
$ py.test --markers
@pytest.mark.env(name): mark test to run only on named environment
@pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see http://pytest.org/latest/skipping.html
@pytest.mark.xfail(condition, reason=None, run=True): mark the the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. See http://pytest.org/latest/skipping.html
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in multiple different argument value sets. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2. see http://pytest.org/latest/parametrize.html for more info and examples.
@pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures
@pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
@pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
Reading markers which were set from multiple places
----------------------------------------------------
.. versionadded: 2.2.2
.. regendoc:wipe
If you are heavily using markers in your test suite you may encounter the case where a marker is applied several times to a test function. From plugin
code you can read over all such settings. Example::
# content of test_mark_three_times.py
import pytest
pytestmark = pytest.mark.glob("module", x=1)
@pytest.mark.glob("class", x=2)
class TestClass:
@pytest.mark.glob("function", x=3)
def test_something(self):
pass
Here we have the marker "glob" applied three times to the same
test function. From a conftest file we can read it like this::
# content of conftest.py
import sys
def pytest_runtest_setup(item):
g = item.keywords.get("glob", None)
if g is not None:
for info in g:
print ("glob args=%s kwargs=%s" %(info.args, info.kwargs))
sys.stdout.flush()
Let's run this without capturing output and see what we get::
$ py.test -q -s
glob args=('function',) kwargs={'x': 3}
glob args=('class',) kwargs={'x': 2}
glob args=('module',) kwargs={'x': 1}
.
marking platform specific tests with pytest
--------------------------------------------------------------
.. regendoc:wipe
Consider you have a test suite which marks tests for particular platforms,
namely ``pytest.mark.osx``, ``pytest.mark.win32`` etc. and you
also have tests that run on all platforms and have no specific
marker. If you now want to have a way to only run the tests
for your particular platform, you could use the following plugin::
# content of conftest.py
#
import sys
import pytest
ALL = set("osx linux2 win32".split())
def pytest_runtest_setup(item):
if isinstance(item, item.Function):
plat = sys.platform
if plat not in item.keywords:
if ALL.intersection(item.keywords):
pytest.skip("cannot run on platform %s" %(plat))
then tests will be skipped if they were specified for a different platform.
Let's do a little test file to show how this looks like::
# content of test_plat.py
import pytest
@pytest.mark.osx
def test_if_apple_is_evil():
pass
@pytest.mark.linux2
def test_if_linux_works():
pass
@pytest.mark.win32
def test_if_win32_crashes():
pass
def test_runs_everywhere():
pass
then you will see two test skipped and two executed tests as expected::
$ py.test -rs # this option reports skip reasons
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 4 items
test_plat.py s.s.
========================= short test summary info ==========================
SKIP [2] /tmp/doc-exec-133/conftest.py:12: cannot run on platform linux2
=================== 2 passed, 2 skipped in 0.01 seconds ====================
Note that if you specify a platform via the marker-command line option like this::
$ py.test -m linux2
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 4 items
test_plat.py .
=================== 3 tests deselected by "-m 'linux2'" ====================
================== 1 passed, 3 deselected in 0.01 seconds ==================
then the unmarked-tests will not be run. It is thus a way to restrict the run to the specific tests.
Automatically adding markers based on test names
--------------------------------------------------------
.. regendoc:wipe
If you a test suite where test function names indicate a certain
type of test, you can implement a hook that automatically defines
markers so that you can use the ``-m`` option with it. Let's look
at this test module::
# content of test_module.py
def test_interface_simple():
assert 0
def test_interface_complex():
assert 0
def test_event_simple():
assert 0
def test_something_else():
assert 0
We want to dynamically define two markers and can do it in a
``conftest.py`` plugin::
# content of conftest.py
import pytest
def pytest_collection_modifyitems(items):
for item in items:
if "interface" in item.nodeid:
item.keywords["interface"] = pytest.mark.interface
elif "event" in item.nodeid:
item.keywords["event"] = pytest.mark.event
We can now use the ``-m option`` to select one set::
$ py.test -m interface --tb=short
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 4 items
test_module.py FF
================================= FAILURES =================================
__________________________ test_interface_simple ___________________________
test_module.py:3: in test_interface_simple
> assert 0
E assert 0
__________________________ test_interface_complex __________________________
test_module.py:6: in test_interface_complex
> assert 0
E assert 0
================== 2 tests deselected by "-m 'interface'" ==================
================== 2 failed, 2 deselected in 0.01 seconds ==================
or to select both "event" and "interface" tests::
$ py.test -m "interface or event" --tb=short
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 4 items
test_module.py FFF
================================= FAILURES =================================
__________________________ test_interface_simple ___________________________
test_module.py:3: in test_interface_simple
> assert 0
E assert 0
__________________________ test_interface_complex __________________________
test_module.py:6: in test_interface_complex
> assert 0
E assert 0
____________________________ test_event_simple _____________________________
test_module.py:9: in test_event_simple
> assert 0
E assert 0
============= 1 tests deselected by "-m 'interface or event'" ==============
================== 3 failed, 1 deselected in 0.02 seconds ==================

View File

@@ -0,0 +1,50 @@
"""
module containing a parametrized tests testing cross-python
serialization via the pickle module.
"""
import py, pytest
pythonlist = ['python2.4', 'python2.5', 'python2.6', 'python2.7', 'python2.8']
@pytest.fixture(params=pythonlist)
def python1(request, tmpdir):
picklefile = tmpdir.join("data.pickle")
return Python(request.param, picklefile)
@pytest.fixture(params=pythonlist)
def python2(request, python1):
return Python(request.param, python1.picklefile)
class Python:
def __init__(self, version, picklefile):
self.pythonpath = py.path.local.sysfind(version)
if not self.pythonpath:
py.test.skip("%r not found" %(version,))
self.picklefile = picklefile
def dumps(self, obj):
dumpfile = self.picklefile.dirpath("dump.py")
dumpfile.write(py.code.Source("""
import pickle
f = open(%r, 'wb')
s = pickle.dump(%r, f)
f.close()
""" % (str(self.picklefile), obj)))
py.process.cmdexec("%s %s" %(self.pythonpath, dumpfile))
def load_and_is_true(self, expression):
loadfile = self.picklefile.dirpath("load.py")
loadfile.write(py.code.Source("""
import pickle
f = open(%r, 'rb')
obj = pickle.load(f)
f.close()
res = eval(%r)
if not res:
raise SystemExit(1)
""" % (str(self.picklefile), expression)))
print (loadfile)
py.process.cmdexec("%s %s" %(self.pythonpath, loadfile))
@pytest.mark.parametrize("obj", [42, {}, {1:3},])
def test_basic_objects(python1, python2, obj):
python1.dumps(obj)
python2.load_and_is_true("obj == %s" % obj)

View File

@@ -27,8 +27,8 @@ now execute the test specification::
nonpython $ py.test test_simple.yml
=========================== test session starts ============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1
collecting ... collected 2 items
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 2 items
test_simple.yml .F
@@ -56,7 +56,7 @@ consulted when reporting in ``verbose`` mode::
nonpython $ py.test -v
=========================== test session starts ============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1 -- /Users/hpk/venv/1/bin/python
platform linux2 -- Python 2.7.3 -- pytest-2.3.4 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 2 items
test_simple.yml:1: usecase: ok PASSED
@@ -67,17 +67,17 @@ consulted when reporting in ``verbose`` mode::
usecase execution failed
spec failed: 'some': 'other'
no further details known at this point.
==================== 1 failed, 1 passed in 0.09 seconds ====================
==================== 1 failed, 1 passed in 0.04 seconds ====================
While developing your custom test collection and execution it's also
interesting to just look at the collection tree::
nonpython $ py.test --collectonly
=========================== test session starts ============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1
collecting ... collected 2 items
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 2 items
<YamlFile 'test_simple.yml'>
<YamlItem 'ok'>
<YamlItem 'hello'>
============================= in 0.08 seconds =============================
============================= in 0.04 seconds =============================

View File

@@ -0,0 +1,40 @@
# content of conftest.py
import pytest
def pytest_collect_file(parent, path):
if path.ext == ".yml" and path.basename.startswith("test"):
return YamlFile(path, parent)
class YamlFile(pytest.File):
def collect(self):
import yaml # we need a yaml parser, e.g. PyYAML
raw = yaml.load(self.fspath.open())
for name, spec in raw.items():
yield YamlItem(name, self, spec)
class YamlItem(pytest.Item):
def __init__(self, name, parent, spec):
super(YamlItem, self).__init__(name, parent)
self.spec = spec
def runtest(self):
for name, value in self.spec.items():
# some custom test execution (dumb example follows)
if name != value:
raise YamlException(self, name, value)
def repr_failure(self, excinfo):
""" called when self.runtest() raises an exception. """
if isinstance(excinfo.value, YamlException):
return "\n".join([
"usecase execution failed",
" spec failed: %r: %r" % excinfo.value.args[1:3],
" no further details known at this point."
])
def reportinfo(self):
return self.fspath, 0, "usecase: %s" % self.name
class YamlException(Exception):
""" custom exception for error reporting. """

View File

@@ -7,60 +7,11 @@ Parametrizing tests
.. currentmodule:: _pytest.python
py.test allows to easily parametrize test functions.
For basic docs, see :ref:`parametrize-basics`.
In the following we provide some examples using
the builtin mechanisms.
.. _parametrizemark:
Simple "decorator" parametrization of a test function
----------------------------------------------------------------------------
.. versionadded:: 2.2
The builtin ``pytest.mark.parametrize`` decorator directly enables
parametrization of arguments for a test function. Here is an example
of a test function that wants to compare that processing some input
results in expected output::
# content of test_expectation.py
import pytest
@pytest.mark.parametrize(("input", "expected"), [
("3+5", 8),
("2+4", 6),
("6*9", 42),
])
def test_eval(input, expected):
assert eval(input) == expected
we parametrize two arguments of the test function so that the test
function is called three times. Let's run it::
$ py.test -q
collecting ... collected 3 items
..F
================================= FAILURES =================================
____________________________ test_eval[6*9-42] _____________________________
input = '6*9', expected = 42
@pytest.mark.parametrize(("input", "expected"), [
("3+5", 8),
("2+4", 6),
("6*9", 42),
])
def test_eval(input, expected):
> assert eval(input) == expected
E assert 54 == 42
E + where 54 = eval('6*9')
test_expectation.py:8: AssertionError
1 failed, 2 passed in 0.03 seconds
As expected only one pair of input/output values fails the simple test function.
Note that there are various ways how you can mark groups of functions,
see :ref:`mark`.
Generating parameters combinations, depending on command line
----------------------------------------------------------------------------
@@ -84,7 +35,7 @@ Now we add a test configuration like this::
help="run all combinations")
def pytest_generate_tests(metafunc):
if 'param1' in metafunc.funcargnames:
if 'param1' in metafunc.fixturenames:
if metafunc.config.option.all:
end = 5
else:
@@ -94,15 +45,12 @@ Now we add a test configuration like this::
This means that we only run 2 tests if we do not pass ``--all``::
$ py.test -q test_compute.py
collecting ... collected 2 items
..
2 passed in 0.02 seconds
We run only two computations, so we see two dots.
let's run the full monty::
$ py.test -q --all
collecting ... collected 5 items
....F
================================= FAILURES =================================
_____________________________ test_compute[4] ______________________________
@@ -114,7 +62,6 @@ let's run the full monty::
E assert 4 < 4
test_compute.py:3: AssertionError
1 failed, 4 passed in 0.03 seconds
As expected when running the full range of ``param1`` values
we'll get an error on the last one.
@@ -122,7 +69,7 @@ we'll get an error on the last one.
A quick port of "testscenarios"
------------------------------------
.. _`test scenarios`: http://bazaar.launchpad.net/~lifeless/testscenarios/trunk/annotate/head%3A/doc/example.py
.. _`test scenarios`: http://pypi.python.org/pypi/testscenarios/
Here is a quick port to run tests configured with `test scenarios`_,
an add-on from Robert Collins for the standard unittest framework. We
@@ -139,7 +86,7 @@ only have to work a bit to construct the correct arguments for pytest's
items = scenario[1].items()
argnames = [x[0] for x in items]
argvalues.append(([x[1] for x in items]))
metafunc.parametrize(argnames, argvalues, ids=idlist)
metafunc.parametrize(argnames, argvalues, ids=idlist, scope="class")
scenario1 = ('basic', {'attribute': 'value'})
scenario2 = ('advanced', {'attribute': 'value2'})
@@ -147,35 +94,44 @@ only have to work a bit to construct the correct arguments for pytest's
class TestSampleWithScenarios:
scenarios = [scenario1, scenario2]
def test_demo(self, attribute):
def test_demo1(self, attribute):
assert isinstance(attribute, str)
def test_demo2(self, attribute):
assert isinstance(attribute, str)
this is a fully self-contained example which you can run with::
$ py.test test_scenarios.py
=========================== test session starts ============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1
collecting ... collected 2 items
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 4 items
test_scenarios.py ..
test_scenarios.py ....
========================= 2 passed in 0.02 seconds =========================
========================= 4 passed in 0.01 seconds =========================
If you just collect tests you'll also nicely see 'advanced' and 'basic' as variants for the test function::
$ py.test --collectonly test_scenarios.py
=========================== test session starts ============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1
collecting ... collected 2 items
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 4 items
<Module 'test_scenarios.py'>
<Class 'TestSampleWithScenarios'>
<Instance '()'>
<Function 'test_demo[basic]'>
<Function 'test_demo[advanced]'>
<Function 'test_demo1[basic]'>
<Function 'test_demo2[basic]'>
<Function 'test_demo1[advanced]'>
<Function 'test_demo2[advanced]'>
============================= in 0.01 seconds =============================
Note that we told ``metafunc.parametrize()`` that your scenario values
should be considered class-scoped. With pytest-2.3 this leads to a
resource-based ordering.
Deferring the setup of parametrized resources
---------------------------------------------------
@@ -200,9 +156,10 @@ the ``test_db_initialized`` function and also implements a factory that
creates a database object for the actual test invocations::
# content of conftest.py
import pytest
def pytest_generate_tests(metafunc):
if 'db' in metafunc.funcargnames:
if 'db' in metafunc.fixturenames:
metafunc.parametrize("db", ['d1', 'd2'], indirect=True)
class DB1:
@@ -210,7 +167,8 @@ creates a database object for the actual test invocations::
class DB2:
"alternative database object"
def pytest_funcarg__db(request):
@pytest.fixture
def db(request):
if request.param == "d1":
return DB1()
elif request.param == "d2":
@@ -222,23 +180,22 @@ Let's first see how it looks like at collection time::
$ py.test test_backends.py --collectonly
=========================== test session starts ============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1
collecting ... collected 2 items
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 2 items
<Module 'test_backends.py'>
<Function 'test_db_initialized[d1]'>
<Function 'test_db_initialized[d2]'>
============================= in 0.01 seconds =============================
============================= in 0.00 seconds =============================
And then when we run the test::
$ py.test -q test_backends.py
collecting ... collected 2 items
.F
================================= FAILURES =================================
_________________________ test_db_initialized[d2] __________________________
db = <conftest.DB2 instance at 0x10131c6c8>
db = <conftest.DB2 instance at 0x13cbcb0>
def test_db_initialized(db):
# a dummy test
@@ -247,9 +204,8 @@ And then when we run the test::
E Failed: deliberately failing for demo purposes
test_backends.py:6: Failed
1 failed, 1 passed in 0.02 seconds
The first invocation with ``db == "DB1"`` passed while the second with ``db == "DB2"`` failed. Our ``pytest_funcarg__db`` factory has instantiated each of the DB values during the setup phase while the ``pytest_generate_tests`` generated two according calls to the ``test_db_initialized`` during the collection phase.
The first invocation with ``db == "DB1"`` passed while the second with ``db == "DB2"`` failed. Our ``db`` fixture function has instantiated each of the DB values during the setup phase while the ``pytest_generate_tests`` generated two according calls to the ``test_db_initialized`` during the collection phase.
.. regendoc:wipe
@@ -290,27 +246,25 @@ Our test generator looks up a class-level definition which specifies which
argument sets to use for each test function. Let's run it::
$ py.test -q
collecting ... collected 3 items
F..
================================= FAILURES =================================
________________________ TestClass.test_equals[1-2] ________________________
self = <test_parametrize.TestClass instance at 0x101320200>, a = 1, b = 2
self = <test_parametrize.TestClass instance at 0x24e6d88>, a = 1, b = 2
def test_equals(self, a, b):
> assert a == b
E assert 1 == 2
test_parametrize.py:18: AssertionError
1 failed, 2 passed in 0.03 seconds
Indirect parametrization with multiple resources
Indirect parametrization with multiple fixtures
--------------------------------------------------------------
Here is a stripped down real-life example of using parametrized
testing for testing serialization, invoking different python interpreters.
We define a ``test_basic_objects`` function which is to be run
with different sets of arguments for its three arguments:
testing for testing serialization of objects between different python
interpreters. We define a ``test_basic_objects`` function which
is to be run with different sets of arguments for its three arguments:
* ``python1``: first python interpreter, run to pickle-dump an object to a file
* ``python2``: second interpreter, run to pickle-load an object from a file
@@ -321,9 +275,6 @@ with different sets of arguments for its three arguments:
Running it results in some skips if we don't have all the python interpreters installed and otherwise runs all combinations (5 interpreters times 5 interpreters times 3 objects to serialize/deserialize)::
. $ py.test -rs -q multipython.py
collecting ... collected 75 items
ssssssssssssssssss.........ssssss.........ssssss.........ssssssssssssssssss
............sss............sss............sss............ssssssssssssssssss
========================= short test summary info ==========================
SKIP [24] /Users/hpk/p/pytest/doc/example/multipython.py:36: 'python2.8' not found
SKIP [24] /Users/hpk/p/pytest/doc/example/multipython.py:36: 'python2.4' not found
27 passed, 48 skipped in 3.01 seconds
SKIP [27] /home/hpk/p/pytest/doc/en/example/multipython.py:21: 'python2.8' not found

View File

@@ -0,0 +1,16 @@
import sys
import pytest
py3 = sys.version_info[0] >= 3
class DummyCollector(pytest.collect.File):
def collect(self):
return []
def pytest_pycollect_makemodule(path, parent):
bn = path.basename
if "py3" in bn and not py3 or ("py2" in bn and py3):
return DummyCollector(path, parent=parent)

View File

@@ -0,0 +1,7 @@
def test_exception_syntax():
try:
0/0
except ZeroDivisionError, e:
pass

View File

@@ -0,0 +1,7 @@
def test_exception_syntax():
try:
0/0
except ZeroDivisionError as e:
pass

View File

@@ -43,8 +43,8 @@ then the test collection looks like this::
$ py.test --collectonly
=========================== test session starts ============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1
collecting ... collected 2 items
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 2 items
<Module 'check_myapp.py'>
<Class 'CheckMyApp'>
<Instance '()'>
@@ -82,8 +82,8 @@ You can always peek at the collection tree without running tests like this::
. $ py.test --collectonly pythoncollection.py
=========================== test session starts ============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1
collecting ... collected 3 items
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 3 items
<Module 'pythoncollection.py'>
<Function 'test_function'>
<Class 'TestClass'>
@@ -92,3 +92,55 @@ You can always peek at the collection tree without running tests like this::
<Function 'test_anothermethod'>
============================= in 0.01 seconds =============================
customizing test collection to find all .py files
---------------------------------------------------------
.. regendoc:wipe
You can easily instruct py.test to discover tests from every python file::
# content of pytest.ini
[pytest]
python_files = *.py
However, many projects will have a ``setup.py`` which they don't want to be imported. Moreover, there may files only importable by a specific python version.
For such cases you can dynamically define files to be ignored by listing
them in a ``conftest.py`` file::
# content of conftest.py
import sys
collect_ignore = ["setup.py"]
if sys.version_info[0] > 2:
collect_ignore.append("pkg/module_py2.py")
And then if you have a module file like this::
# content of pkg/module_py2.py
def test_only_on_python2():
try:
assert 0
except Exception, e:
pass
and a setup.py dummy file like this::
# content of setup.py
0/0 # will raise exeption if imported
then a pytest run on python2 will find the one test when run with a python2
interpreters and will leave out the setup.py file::
$ py.test --collectonly
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 1 items
<Module 'pkg/module_py2.py'>
<Function 'test_only_on_python2'>
============================= in 0.01 seconds =============================
If you run with a Python3 interpreter the moduled added through the conftest.py file will not be considered for test collection.

View File

@@ -13,8 +13,8 @@ get on the terminal - we are working on that):
assertion $ py.test failure_demo.py
=========================== test session starts ============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1
collecting ... collected 39 items
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 39 items
failure_demo.py FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
@@ -30,7 +30,7 @@ get on the terminal - we are working on that):
failure_demo.py:15: AssertionError
_________________________ TestFailing.test_simple __________________________
self = <failure_demo.TestFailing object at 0x101356250>
self = <failure_demo.TestFailing object at 0x2ad4550>
def test_simple(self):
def f():
@@ -40,13 +40,13 @@ get on the terminal - we are working on that):
> assert f() == g()
E assert 42 == 43
E + where 42 = <function f at 0x101328848>()
E + and 43 = <function g at 0x1013288c0>()
E + where 42 = <function f at 0x2a7f578>()
E + and 43 = <function g at 0x2a7f5f0>()
failure_demo.py:28: AssertionError
____________________ TestFailing.test_simple_multiline _____________________
self = <failure_demo.TestFailing object at 0x101356810>
self = <failure_demo.TestFailing object at 0x2a81e50>
def test_simple_multiline(self):
otherfunc_multi(
@@ -66,19 +66,19 @@ get on the terminal - we are working on that):
failure_demo.py:11: AssertionError
___________________________ TestFailing.test_not ___________________________
self = <failure_demo.TestFailing object at 0x101356a10>
self = <failure_demo.TestFailing object at 0x2a72b50>
def test_not(self):
def f():
return 42
> assert not f()
E assert not 42
E + where 42 = <function f at 0x101328758>()
E + where 42 = <function f at 0x2a7f9b0>()
failure_demo.py:38: AssertionError
_________________ TestSpecialisedExplanations.test_eq_text _________________
self = <failure_demo.TestSpecialisedExplanations object at 0x101356c50>
self = <failure_demo.TestSpecialisedExplanations object at 0x2a6eb50>
def test_eq_text(self):
> assert 'spam' == 'eggs'
@@ -89,7 +89,7 @@ get on the terminal - we are working on that):
failure_demo.py:42: AssertionError
_____________ TestSpecialisedExplanations.test_eq_similar_text _____________
self = <failure_demo.TestSpecialisedExplanations object at 0x1013542d0>
self = <failure_demo.TestSpecialisedExplanations object at 0x2b07cd0>
def test_eq_similar_text(self):
> assert 'foo 1 bar' == 'foo 2 bar'
@@ -102,7 +102,7 @@ get on the terminal - we are working on that):
failure_demo.py:45: AssertionError
____________ TestSpecialisedExplanations.test_eq_multiline_text ____________
self = <failure_demo.TestSpecialisedExplanations object at 0x101354590>
self = <failure_demo.TestSpecialisedExplanations object at 0x2a68050>
def test_eq_multiline_text(self):
> assert 'foo\nspam\nbar' == 'foo\neggs\nbar'
@@ -115,7 +115,7 @@ get on the terminal - we are working on that):
failure_demo.py:48: AssertionError
______________ TestSpecialisedExplanations.test_eq_long_text _______________
self = <failure_demo.TestSpecialisedExplanations object at 0x101354710>
self = <failure_demo.TestSpecialisedExplanations object at 0x2ad2990>
def test_eq_long_text(self):
a = '1'*100 + 'a' + '2'*100
@@ -132,7 +132,7 @@ get on the terminal - we are working on that):
failure_demo.py:53: AssertionError
_________ TestSpecialisedExplanations.test_eq_long_text_multiline __________
self = <failure_demo.TestSpecialisedExplanations object at 0x1013529d0>
self = <failure_demo.TestSpecialisedExplanations object at 0x2ad6c10>
def test_eq_long_text_multiline(self):
a = '1\n'*100 + 'a' + '2\n'*100
@@ -156,7 +156,7 @@ get on the terminal - we are working on that):
failure_demo.py:58: AssertionError
_________________ TestSpecialisedExplanations.test_eq_list _________________
self = <failure_demo.TestSpecialisedExplanations object at 0x101352750>
self = <failure_demo.TestSpecialisedExplanations object at 0x2a81c50>
def test_eq_list(self):
> assert [0, 1, 2] == [0, 1, 3]
@@ -166,7 +166,7 @@ get on the terminal - we are working on that):
failure_demo.py:61: AssertionError
______________ TestSpecialisedExplanations.test_eq_list_long _______________
self = <failure_demo.TestSpecialisedExplanations object at 0x101352ad0>
self = <failure_demo.TestSpecialisedExplanations object at 0x2a69f50>
def test_eq_list_long(self):
a = [0]*100 + [1] + [3]*100
@@ -178,7 +178,7 @@ get on the terminal - we are working on that):
failure_demo.py:66: AssertionError
_________________ TestSpecialisedExplanations.test_eq_dict _________________
self = <failure_demo.TestSpecialisedExplanations object at 0x101352b90>
self = <failure_demo.TestSpecialisedExplanations object at 0x2ad5f50>
def test_eq_dict(self):
> assert {'a': 0, 'b': 1} == {'a': 0, 'b': 2}
@@ -191,7 +191,7 @@ get on the terminal - we are working on that):
failure_demo.py:69: AssertionError
_________________ TestSpecialisedExplanations.test_eq_set __________________
self = <failure_demo.TestSpecialisedExplanations object at 0x101352fd0>
self = <failure_demo.TestSpecialisedExplanations object at 0x2ad4410>
def test_eq_set(self):
> assert set([0, 10, 11, 12]) == set([0, 20, 21])
@@ -207,7 +207,7 @@ get on the terminal - we are working on that):
failure_demo.py:72: AssertionError
_____________ TestSpecialisedExplanations.test_eq_longer_list ______________
self = <failure_demo.TestSpecialisedExplanations object at 0x101352b50>
self = <failure_demo.TestSpecialisedExplanations object at 0x2ad2d50>
def test_eq_longer_list(self):
> assert [1,2] == [1,2,3]
@@ -217,7 +217,7 @@ get on the terminal - we are working on that):
failure_demo.py:75: AssertionError
_________________ TestSpecialisedExplanations.test_in_list _________________
self = <failure_demo.TestSpecialisedExplanations object at 0x1013522d0>
self = <failure_demo.TestSpecialisedExplanations object at 0x2a81310>
def test_in_list(self):
> assert 1 in [0, 2, 3, 4, 5]
@@ -226,7 +226,7 @@ get on the terminal - we are working on that):
failure_demo.py:78: AssertionError
__________ TestSpecialisedExplanations.test_not_in_text_multiline __________
self = <failure_demo.TestSpecialisedExplanations object at 0x101351390>
self = <failure_demo.TestSpecialisedExplanations object at 0x2a697d0>
def test_not_in_text_multiline(self):
text = 'some multiline\ntext\nwhich\nincludes foo\nand a\ntail'
@@ -244,7 +244,7 @@ get on the terminal - we are working on that):
failure_demo.py:82: AssertionError
___________ TestSpecialisedExplanations.test_not_in_text_single ____________
self = <failure_demo.TestSpecialisedExplanations object at 0x101351410>
self = <failure_demo.TestSpecialisedExplanations object at 0x2ad4d10>
def test_not_in_text_single(self):
text = 'single foo line'
@@ -257,7 +257,7 @@ get on the terminal - we are working on that):
failure_demo.py:86: AssertionError
_________ TestSpecialisedExplanations.test_not_in_text_single_long _________
self = <failure_demo.TestSpecialisedExplanations object at 0x101351510>
self = <failure_demo.TestSpecialisedExplanations object at 0x2ad2fd0>
def test_not_in_text_single_long(self):
text = 'head ' * 50 + 'foo ' + 'tail ' * 20
@@ -270,7 +270,7 @@ get on the terminal - we are working on that):
failure_demo.py:90: AssertionError
______ TestSpecialisedExplanations.test_not_in_text_single_long_term _______
self = <failure_demo.TestSpecialisedExplanations object at 0x101351a10>
self = <failure_demo.TestSpecialisedExplanations object at 0x2a6f410>
def test_not_in_text_single_long_term(self):
text = 'head ' * 50 + 'f'*70 + 'tail ' * 20
@@ -289,7 +289,7 @@ get on the terminal - we are working on that):
i = Foo()
> assert i.b == 2
E assert 1 == 2
E + where 1 = <failure_demo.Foo object at 0x101351b50>.b
E + where 1 = <failure_demo.Foo object at 0x2a81850>.b
failure_demo.py:101: AssertionError
_________________________ test_attribute_instance __________________________
@@ -299,8 +299,8 @@ get on the terminal - we are working on that):
b = 1
> assert Foo().b == 2
E assert 1 == 2
E + where 1 = <failure_demo.Foo object at 0x101351810>.b
E + where <failure_demo.Foo object at 0x101351810> = <class 'failure_demo.Foo'>()
E + where 1 = <failure_demo.Foo object at 0x2ad4bd0>.b
E + where <failure_demo.Foo object at 0x2ad4bd0> = <class 'failure_demo.Foo'>()
failure_demo.py:107: AssertionError
__________________________ test_attribute_failure __________________________
@@ -316,7 +316,7 @@ get on the terminal - we are working on that):
failure_demo.py:116:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <failure_demo.Foo object at 0x101351c50>
self = <failure_demo.Foo object at 0x2ad26d0>
def _get_b(self):
> raise Exception('Failed to get attrib')
@@ -332,15 +332,15 @@ get on the terminal - we are working on that):
b = 2
> assert Foo().b == Bar().b
E assert 1 == 2
E + where 1 = <failure_demo.Foo object at 0x101351f10>.b
E + where <failure_demo.Foo object at 0x101351f10> = <class 'failure_demo.Foo'>()
E + and 2 = <failure_demo.Bar object at 0x1013519d0>.b
E + where <failure_demo.Bar object at 0x1013519d0> = <class 'failure_demo.Bar'>()
E + where 1 = <failure_demo.Foo object at 0x2a6ff10>.b
E + where <failure_demo.Foo object at 0x2a6ff10> = <class 'failure_demo.Foo'>()
E + and 2 = <failure_demo.Bar object at 0x2a6fd50>.b
E + where <failure_demo.Bar object at 0x2a6fd50> = <class 'failure_demo.Bar'>()
failure_demo.py:124: AssertionError
__________________________ TestRaises.test_raises __________________________
self = <failure_demo.TestRaises instance at 0x101373710>
self = <failure_demo.TestRaises instance at 0x2a75c68>
def test_raises(self):
s = 'qwe'
@@ -352,10 +352,10 @@ get on the terminal - we are working on that):
> int(s)
E ValueError: invalid literal for int() with base 10: 'qwe'
<0-codegen /Users/hpk/p/pytest/_pytest/python.py:958>:1: ValueError
<0-codegen /home/hpk/p/pytest/.tox/regen/lib/python2.7/site-packages/_pytest/python.py:851>:1: ValueError
______________________ TestRaises.test_raises_doesnt _______________________
self = <failure_demo.TestRaises instance at 0x101334f38>
self = <failure_demo.TestRaises instance at 0x2adf3f8>
def test_raises_doesnt(self):
> raises(IOError, "int('3')")
@@ -364,7 +364,7 @@ get on the terminal - we are working on that):
failure_demo.py:136: Failed
__________________________ TestRaises.test_raise ___________________________
self = <failure_demo.TestRaises instance at 0x10136d950>
self = <failure_demo.TestRaises instance at 0x2af1830>
def test_raise(self):
> raise ValueError("demo error")
@@ -373,7 +373,7 @@ get on the terminal - we are working on that):
failure_demo.py:139: ValueError
________________________ TestRaises.test_tupleerror ________________________
self = <failure_demo.TestRaises instance at 0x101367758>
self = <failure_demo.TestRaises instance at 0x2ae5290>
def test_tupleerror(self):
> a,b = [1]
@@ -382,7 +382,7 @@ get on the terminal - we are working on that):
failure_demo.py:142: ValueError
______ TestRaises.test_reinterpret_fails_with_print_for_the_fun_of_it ______
self = <failure_demo.TestRaises instance at 0x10136a4d0>
self = <failure_demo.TestRaises instance at 0x2ae2878>
def test_reinterpret_fails_with_print_for_the_fun_of_it(self):
l = [1,2,3]
@@ -395,11 +395,11 @@ get on the terminal - we are working on that):
l is [1, 2, 3]
________________________ TestRaises.test_some_error ________________________
self = <failure_demo.TestRaises instance at 0x1013692d8>
self = <failure_demo.TestRaises instance at 0x2af0e18>
def test_some_error(self):
> if namenotexi:
E NameError: global name 'namenotexi' is not defined
E NameError: global name 'namenotexi' is not defined
failure_demo.py:150: NameError
____________________ test_dynamic_compile_shows_nicely _____________________
@@ -420,10 +420,10 @@ get on the terminal - we are working on that):
> assert 1 == 0
E assert 1 == 0
<2-codegen 'abc-123' /Users/hpk/p/pytest/doc/example/assertion/failure_demo.py:162>:2: AssertionError
<2-codegen 'abc-123' /home/hpk/p/pytest/doc/en/example/assertion/failure_demo.py:162>:2: AssertionError
____________________ TestMoreErrors.test_complex_error _____________________
self = <failure_demo.TestMoreErrors instance at 0x1013730e0>
self = <failure_demo.TestMoreErrors instance at 0x2adbc68>
def test_complex_error(self):
def f():
@@ -452,7 +452,7 @@ get on the terminal - we are working on that):
failure_demo.py:5: AssertionError
___________________ TestMoreErrors.test_z1_unpack_error ____________________
self = <failure_demo.TestMoreErrors instance at 0x101368290>
self = <failure_demo.TestMoreErrors instance at 0x2aee1b8>
def test_z1_unpack_error(self):
l = []
@@ -462,7 +462,7 @@ get on the terminal - we are working on that):
failure_demo.py:179: ValueError
____________________ TestMoreErrors.test_z2_type_error _____________________
self = <failure_demo.TestMoreErrors instance at 0x1013610e0>
self = <failure_demo.TestMoreErrors instance at 0x2ae27a0>
def test_z2_type_error(self):
l = 3
@@ -472,19 +472,19 @@ get on the terminal - we are working on that):
failure_demo.py:183: TypeError
______________________ TestMoreErrors.test_startswith ______________________
self = <failure_demo.TestMoreErrors instance at 0x101361ea8>
self = <failure_demo.TestMoreErrors instance at 0x2ae1128>
def test_startswith(self):
s = "123"
g = "456"
> assert s.startswith(g)
E assert <built-in method startswith of str object at 0x101357a58>('456')
E + where <built-in method startswith of str object at 0x101357a58> = '123'.startswith
E assert <built-in method startswith of str object at 0x2adc918>('456')
E + where <built-in method startswith of str object at 0x2adc918> = '123'.startswith
failure_demo.py:188: AssertionError
__________________ TestMoreErrors.test_startswith_nested ___________________
self = <failure_demo.TestMoreErrors instance at 0x101368128>
self = <failure_demo.TestMoreErrors instance at 0x2c720e0>
def test_startswith_nested(self):
def f():
@@ -492,15 +492,15 @@ get on the terminal - we are working on that):
def g():
return "456"
> assert f().startswith(g())
E assert <built-in method startswith of str object at 0x101357a58>('456')
E + where <built-in method startswith of str object at 0x101357a58> = '123'.startswith
E + where '123' = <function f at 0x101339938>()
E + and '456' = <function g at 0x101339cf8>()
E assert <built-in method startswith of str object at 0x2adc918>('456')
E + where <built-in method startswith of str object at 0x2adc918> = '123'.startswith
E + where '123' = <function f at 0x2af5b90>()
E + and '456' = <function g at 0x2af5c08>()
failure_demo.py:195: AssertionError
_____________________ TestMoreErrors.test_global_func ______________________
self = <failure_demo.TestMoreErrors instance at 0x101336758>
self = <failure_demo.TestMoreErrors instance at 0x2c725f0>
def test_global_func(self):
> assert isinstance(globf(42), float)
@@ -510,18 +510,18 @@ get on the terminal - we are working on that):
failure_demo.py:198: AssertionError
_______________________ TestMoreErrors.test_instance _______________________
self = <failure_demo.TestMoreErrors instance at 0x1013678c0>
self = <failure_demo.TestMoreErrors instance at 0x2a67ab8>
def test_instance(self):
self.x = 6*7
> assert self.x != 42
E assert 42 != 42
E + where 42 = <failure_demo.TestMoreErrors instance at 0x1013678c0>.x
E + where 42 = <failure_demo.TestMoreErrors instance at 0x2a67ab8>.x
failure_demo.py:202: AssertionError
_______________________ TestMoreErrors.test_compare ________________________
self = <failure_demo.TestMoreErrors instance at 0x101366a28>
self = <failure_demo.TestMoreErrors instance at 0x2af8710>
def test_compare(self):
> assert globf(10) < 5
@@ -531,7 +531,7 @@ get on the terminal - we are working on that):
failure_demo.py:205: AssertionError
_____________________ TestMoreErrors.test_try_finally ______________________
self = <failure_demo.TestMoreErrors instance at 0x1013628c0>
self = <failure_demo.TestMoreErrors instance at 0x2af03f8>
def test_try_finally(self):
x = 1
@@ -540,4 +540,4 @@ get on the terminal - we are working on that):
E assert 1 == 0
failure_demo.py:210: AssertionError
======================== 39 failed in 0.41 seconds =========================
======================== 39 failed in 0.25 seconds =========================

680
doc/en/example/simple.txt Normal file
View File

@@ -0,0 +1,680 @@
.. highlightlang:: python
Basic patterns and examples
==========================================================
Pass different values to a test function, depending on command line options
----------------------------------------------------------------------------
.. regendoc:wipe
Suppose we want to write a test that depends on a command line option.
Here is a basic pattern how to achieve this::
# content of test_sample.py
def test_answer(cmdopt):
if cmdopt == "type1":
print ("first")
elif cmdopt == "type2":
print ("second")
assert 0 # to see what was printed
For this to work we need to add a command line option and
provide the ``cmdopt`` through a :ref:`fixture function <fixture function>`::
# content of conftest.py
import pytest
def pytest_addoption(parser):
parser.addoption("--cmdopt", action="store", default="type1",
help="my option: type1 or type2")
@pytest.fixture
def cmdopt(request):
return request.config.getoption("--cmdopt")
Let's run this without supplying our new option::
$ py.test -q test_sample.py
F
================================= FAILURES =================================
_______________________________ test_answer ________________________________
cmdopt = 'type1'
def test_answer(cmdopt):
if cmdopt == "type1":
print ("first")
elif cmdopt == "type2":
print ("second")
> assert 0 # to see what was printed
E assert 0
test_sample.py:6: AssertionError
----------------------------- Captured stdout ------------------------------
first
And now with supplying a command line option::
$ py.test -q --cmdopt=type2
F
================================= FAILURES =================================
_______________________________ test_answer ________________________________
cmdopt = 'type2'
def test_answer(cmdopt):
if cmdopt == "type1":
print ("first")
elif cmdopt == "type2":
print ("second")
> assert 0 # to see what was printed
E assert 0
test_sample.py:6: AssertionError
----------------------------- Captured stdout ------------------------------
second
You can see that the command line option arrived in our test. This
completes the basic pattern. However, one often rather wants to process
command line options outside of the test and rather pass in different or
more complex objects.
Dynamically adding command line options
--------------------------------------------------------------
.. regendoc:wipe
Through :confval:`addopts` you can statically add command line
options for your project. You can also dynamically modify
the command line arguments before they get processed::
# content of conftest.py
import sys
def pytest_cmdline_preparse(args):
if 'xdist' in sys.modules: # pytest-xdist plugin
import multiprocessing
num = max(multiprocessing.cpu_count() / 2, 1)
args[:] = ["-n", str(num)] + args
If you have the :ref:`xdist plugin <xdist>` installed
you will now always perform test runs using a number
of subprocesses close to your CPU. Running in an empty
directory with the above conftest.py::
$ py.test
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 0 items
============================= in 0.00 seconds =============================
.. _`excontrolskip`:
Control skipping of tests according to command line option
--------------------------------------------------------------
.. regendoc:wipe
Here is a ``conftest.py`` file adding a ``--runslow`` command
line option to control skipping of ``slow`` marked tests::
# content of conftest.py
import pytest
def pytest_addoption(parser):
parser.addoption("--runslow", action="store_true",
help="run slow tests")
def pytest_runtest_setup(item):
if 'slow' in item.keywords and not item.config.getoption("--runslow"):
pytest.skip("need --runslow option to run")
We can now write a test module like this::
# content of test_module.py
import pytest
slow = pytest.mark.slow
def test_func_fast():
pass
@slow
def test_func_slow():
pass
and when running it will see a skipped "slow" test::
$ py.test -rs # "-rs" means report details on the little 's'
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 2 items
test_module.py .s
========================= short test summary info ==========================
SKIP [1] /tmp/doc-exec-138/conftest.py:9: need --runslow option to run
=================== 1 passed, 1 skipped in 0.01 seconds ====================
Or run it including the ``slow`` marked test::
$ py.test --runslow
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 2 items
test_module.py ..
========================= 2 passed in 0.01 seconds =========================
Writing well integrated assertion helpers
--------------------------------------------------
.. regendoc:wipe
If you have a test helper function called from a test you can
use the ``pytest.fail`` marker to fail a test with a certain message.
The test support function will not show up in the traceback if you
set the ``__tracebackhide__`` option somewhere in the helper function.
Example::
# content of test_checkconfig.py
import pytest
def checkconfig(x):
__tracebackhide__ = True
if not hasattr(x, "config"):
pytest.fail("not configured: %s" %(x,))
def test_something():
checkconfig(42)
The ``__tracebackhide__`` setting influences py.test showing
of tracebacks: the ``checkconfig`` function will not be shown
unless the ``--fulltrace`` command line option is specified.
Let's run our little function::
$ py.test -q test_checkconfig.py
F
================================= FAILURES =================================
______________________________ test_something ______________________________
def test_something():
> checkconfig(42)
E Failed: not configured: 42
test_checkconfig.py:8: Failed
Detect if running from within a py.test run
--------------------------------------------------------------
.. regendoc:wipe
Usually it is a bad idea to make application code
behave differently if called from a test. But if you
absolutely must find out if your application code is
running from a test you can do something like this::
# content of conftest.py
def pytest_configure(config):
import sys
sys._called_from_test = True
def pytest_unconfigure(config):
del sys._called_from_test
and then check for the ``sys._called_from_test`` flag::
if hasattr(sys, '_called_from_test'):
# called from within a test run
else:
# called "normally"
accordingly in your application. It's also a good idea
to use your own application module rather than ``sys``
for handling flag.
Adding info to test report header
--------------------------------------------------------------
.. regendoc:wipe
It's easy to present extra information in a py.test run::
# content of conftest.py
def pytest_report_header(config):
return "project deps: mylib-1.1"
which will add the string to the test header accordingly::
$ py.test
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
project deps: mylib-1.1
collected 0 items
============================= in 0.00 seconds =============================
.. regendoc:wipe
You can also return a list of strings which will be considered as several
lines of information. You can of course also make the amount of reporting
information on e.g. the value of ``config.option.verbose`` so that
you present more information appropriately::
# content of conftest.py
def pytest_report_header(config):
if config.option.verbose > 0:
return ["info1: did you know that ...", "did you?"]
which will add info only when run with "--v"::
$ py.test -v
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4 -- /home/hpk/p/pytest/.tox/regen/bin/python
info1: did you know that ...
did you?
collecting ... collected 0 items
============================= in 0.00 seconds =============================
and nothing when run plainly::
$ py.test
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 0 items
============================= in 0.00 seconds =============================
profiling test duration
--------------------------
.. regendoc:wipe
.. versionadded: 2.2
If you have a slow running large test suite you might want to find
out which tests are the slowest. Let's make an artifical test suite::
# content of test_some_are_slow.py
import time
def test_funcfast():
pass
def test_funcslow1():
time.sleep(0.1)
def test_funcslow2():
time.sleep(0.2)
Now we can profile which test functions execute the slowest::
$ py.test --durations=3
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 3 items
test_some_are_slow.py ...
========================= slowest 3 test durations =========================
0.20s call test_some_are_slow.py::test_funcslow2
0.10s call test_some_are_slow.py::test_funcslow1
0.00s call test_some_are_slow.py::test_funcfast
========================= 3 passed in 0.31 seconds =========================
incremental testing - test steps
---------------------------------------------------
.. regendoc:wipe
Sometimes you may have a testing situation which consists of a series
of test steps. If one step fails it makes no sense to execute further
steps as they are all expected to fail anyway and their tracebacks
add no insight. Here is a simple ``conftest.py`` file which introduces
an ``incremental`` marker which is to be used on classes::
# content of conftest.py
import pytest
def pytest_runtest_makereport(item, call):
if "incremental" in item.keywords:
if call.excinfo is not None:
parent = item.parent
parent._previousfailed = item
def pytest_runtest_setup(item):
if "incremental" in item.keywords:
previousfailed = getattr(item.parent, "_previousfailed", None)
if previousfailed is not None:
pytest.xfail("previous test failed (%s)" %previousfailed.name)
These two hook implementations work together to abort incremental-marked
tests in a class. Here is a test module example::
# content of test_step.py
import pytest
@pytest.mark.incremental
class TestUserHandling:
def test_login(self):
pass
def test_modification(self):
assert 0
def test_deletion(self):
pass
def test_normal():
pass
If we run this::
$ py.test -rx
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 4 items
test_step.py .Fx.
================================= FAILURES =================================
____________________ TestUserHandling.test_modification ____________________
self = <test_step.TestUserHandling instance at 0x193bc68>
def test_modification(self):
> assert 0
E assert 0
test_step.py:9: AssertionError
========================= short test summary info ==========================
XFAIL test_step.py::TestUserHandling::()::test_deletion
reason: previous test failed (test_modification)
============== 1 failed, 2 passed, 1 xfailed in 0.02 seconds ===============
We'll see that ``test_deletion`` was not executed because ``test_modification``
failed. It is reported as an "expected failure".
Package/Directory-level fixtures (setups)
-------------------------------------------------------
If you have nested test directories, you can have per-directory fixture scopes
by placing fixture functions in a ``conftest.py`` file in that directory
You can use all types of fixtures including :ref:`autouse fixtures
<autouse fixtures>` which are the equivalent of xUnit's setup/teardown
concept. It's however recommended to have explicit fixture references in your
tests or test classes rather than relying on implicitely executing
setup/teardown functions, especially if they are far away from the actual tests.
Here is a an example for making a ``db`` fixture available in a directory::
# content of a/conftest.py
import pytest
class DB:
pass
@pytest.fixture(scope="session")
def db():
return DB()
and then a test module in that directory::
# content of a/test_db.py
def test_a1(db):
assert 0, db # to show value
another test module::
# content of a/test_db2.py
def test_a2(db):
assert 0, db # to show value
and then a module in a sister directory which will not see
the ``db`` fixture::
# content of b/test_error.py
def test_root(db): # no db here, will error out
pass
We can run this::
$ py.test
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 7 items
test_step.py .Fx.
a/test_db.py F
a/test_db2.py F
b/test_error.py E
================================== ERRORS ==================================
_______________________ ERROR at setup of test_root ________________________
file /tmp/doc-exec-138/b/test_error.py, line 1
def test_root(db): # no db here, will error out
fixture 'db' not found
available fixtures: pytestconfig, recwarn, monkeypatch, capfd, capsys, tmpdir
use 'py.test --fixtures [testpath]' for help on them.
/tmp/doc-exec-138/b/test_error.py:1
================================= FAILURES =================================
____________________ TestUserHandling.test_modification ____________________
self = <test_step.TestUserHandling instance at 0x1492d88>
def test_modification(self):
> assert 0
E assert 0
test_step.py:9: AssertionError
_________________________________ test_a1 __________________________________
db = <conftest.DB instance at 0x1498e60>
def test_a1(db):
> assert 0, db # to show value
E AssertionError: <conftest.DB instance at 0x1498e60>
a/test_db.py:2: AssertionError
_________________________________ test_a2 __________________________________
db = <conftest.DB instance at 0x1498e60>
def test_a2(db):
> assert 0, db # to show value
E AssertionError: <conftest.DB instance at 0x1498e60>
a/test_db2.py:2: AssertionError
========== 3 failed, 2 passed, 1 xfailed, 1 error in 0.04 seconds ==========
The two test modules in the ``a`` directory see the same ``db`` fixture instance
while the one test in the sister-directory ``b`` doesn't see it. We could of course
also define a ``db`` fixture in that sister directory's ``conftest.py`` file.
Note that each fixture is only instantiated if there is a test actually needing
it (unless you use "autouse" fixture which are always executed ahead of the first test
executing).
post-process test reports / failures
---------------------------------------
If you want to postprocess test reports and need access to the executing
environment you can implement a hook that gets called when the test
"report" object is about to be created. Here we write out all failing
test calls and also access a fixture (if it was used by the test) in
case you want to query/look at it during your post processing. In our
case we just write some informations out to a ``failures`` file::
# content of conftest.py
import pytest
import os.path
@pytest.mark.tryfirst
def pytest_runtest_makereport(item, call, __multicall__):
# execute all other hooks to obtain the report object
rep = __multicall__.execute()
# we only look at actual failing test calls, not setup/teardown
if rep.when == "call" and rep.failed:
mode = "a" if os.path.exists("failures") else "w"
with open("failures", mode) as f:
# let's also access a fixture for the fun of it
if "tmpdir" in item.funcargs:
extra = " (%s)" % item.funcargs["tmpdir"]
else:
extra = ""
f.write(rep.nodeid + extra + "\n")
return rep
if you then have failing tests::
# content of test_module.py
def test_fail1(tmpdir):
assert 0
def test_fail2():
assert 0
and run them::
$ py.test test_module.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 2 items
test_module.py FF
================================= FAILURES =================================
________________________________ test_fail1 ________________________________
tmpdir = local('/tmp/pytest-543/test_fail10')
def test_fail1(tmpdir):
> assert 0
E assert 0
test_module.py:2: AssertionError
________________________________ test_fail2 ________________________________
def test_fail2():
> assert 0
E assert 0
test_module.py:4: AssertionError
========================= 2 failed in 0.02 seconds =========================
you will have a "failures" file which contains the failing test ids::
$ cat failures
test_module.py::test_fail1 (/tmp/pytest-543/test_fail10)
test_module.py::test_fail2
Making test result information available in fixtures
-----------------------------------------------------------
.. regendoc:wipe
If you want to make test result reports available in fixture finalizers
here is a little example implemented via a local plugin::
# content of conftest.py
import pytest
@pytest.mark.tryfirst
def pytest_runtest_makereport(item, call, __multicall__):
# execute all other hooks to obtain the report object
rep = __multicall__.execute()
# set an report attribute for each phase of a call, which can
# be "setup", "call", "teardown"
setattr(item, "rep_" + rep.when, rep)
return rep
@pytest.fixture
def something(request):
def fin():
# request.node is an "item" because we use the default
# "function" scope
if request.node.rep_setup.failed:
print "setting up a test failed!", request.node.nodeid
elif request.node.rep_setup.passed:
if request.node.rep_call.failed:
print "executing test failed", request.node.nodeid
request.addfinalizer(fin)
if you then have failing tests::
# content of test_module.py
import pytest
@pytest.fixture
def other():
assert 0
def test_setup_fails(something, other):
pass
def test_call_fails(something):
assert 0
def test_fail2():
assert 0
and run it::
$ py.test -s test_module.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
collected 3 items
test_module.py EFF
================================== ERRORS ==================================
____________________ ERROR at setup of test_setup_fails ____________________
@pytest.fixture
def other():
> assert 0
E assert 0
test_module.py:6: AssertionError
================================= FAILURES =================================
_____________________________ test_call_fails ______________________________
something = None
def test_call_fails(something):
> assert 0
E assert 0
test_module.py:12: AssertionError
________________________________ test_fail2 ________________________________
def test_fail2():
> assert 0
E assert 0
test_module.py:15: AssertionError
==================== 2 failed, 1 error in 0.01 seconds =====================
setting up a test failed! test_module.py::test_setup_fails
executing test failed test_module.py::test_call_fails
You'll see that the fixture finalizers could use the precise reporting
information.

Some files were not shown because too many files have changed in this diff Show More