2 new changesets in pytest: http://bitbucket.org/hpk42/pytest/changeset/1eb351cf53d9/ changeset: r2168:1eb351cf53d9 user: hpk42 date: 2011-03-05 13:08:43 summary: improve and clarify skipping docs affected #: 2 files (1.1 KB)
--- a/ISSUES.txt Sat Mar 05 12:11:35 2011 +0100 +++ b/ISSUES.txt Sat Mar 05 13:08:43 2011 +0100 @@ -88,6 +88,16 @@ on common spellings for operating systems and python interpreter versions. +pytest.mark.xfail signature change +------------------------------------------------------- +tags: feature 2.1 + +change to pytest.mark.xfail(reason, (optional)condition) +to better implement the word meaning. It also signals +better that we always have some kind of an implementation +reason that can be formualated. +Compatibility? Maybe rename to "pytest.mark.xfail"? + introduce py.test.mark registration ----------------------------------------- tags: feature 2.1 --- a/doc/skipping.txt Sat Mar 05 12:11:35 2011 +0100 +++ b/doc/skipping.txt Sat Mar 05 13:08:43 2011 +0100 @@ -1,20 +1,22 @@ - .. _`skip and xfail`: -skip and xfail mechanisms +skip and xfail: dealing with tests that can not succeed ===================================================================== -You can skip or "xfail" test functions, either by marking functions -with a decorator or by calling the ``pytest.skip|xfail`` functions. +If you have test functions that cannot be run on certain platforms +or that you expect to fail you can mark them accordingly or you +may call helper functions during execution of setup or test functions. -A *skip* means that you expect your test to pass unless a certain configuration or condition (e.g. wrong Python interpreter, missing dependency) prevents it to run. And *xfail* means that you expect your test to fail because there is an -implementation problem. py.test counts and lists *xfailing* tests separately -and it is possible to give additional information, such as bug number or a URL. +A *skip* means that you expect your test to pass unless a certain +configuration or condition (e.g. wrong Python interpreter, missing +dependency) prevents it to run. And *xfail* means that your test +can run but you expect it to fail because there is an implementation problem. -Detailed information about skipped/xfailed tests is by default not shown -at the end of a test run to avoid cluttering the output. You can use -the ``-r`` option to see details corresponding to the "short" letters -shown in the test progress:: +py.test counts and lists *skip* and *xfail* tests separately. However, +detailed information about skipped/xfailed tests is not shown by default +to avoid cluttering the output. You can use the ``-r`` option to see +details corresponding to the "short" letters shown in the test +progress:: py.test -rxs # show extra info on skips and xfails @@ -22,7 +24,7 @@ .. _skipif: -Skipping a single function +Marking a test function to be skipped ------------------------------------------- Here is an example of marking a test function to be skipped @@ -34,9 +36,9 @@ ... During test function setup the skipif condition is -evaluated by calling ``eval(expr, namespace)``. The namespace -contains all the module globals of the test function so that -you can for example check for versions:: +evaluated by calling ``eval('sys.version_info >= (3,0)', namespace)``. +(*New in version 2.0.2*) The namespace contains all the module globals of the test function so that +you can for example check for versions of a module you are using:: import mymodule @@ -44,23 +46,15 @@ def test_function(): ... -The test function will be skipped and not run if -mymodule is below the specified version. The reason +The test function will not be run ("skipped") if +``mymodule`` is below the specified version. The reason for specifying the condition as a string is mainly that -you can see more detailed reporting of xfail/skip reasons. +py.test can report a summary of skip conditions. +For information on the construction of the ``namespace`` +see `evaluation of skipif/xfail conditions`_. -Actually, the namespace is first initialized by -putting the ``sys`` and ``os`` modules and the test -``config`` object into it. And is then updated with -the module globals. The latter allows you to skip based -on a test configuration value:: - - @pytest.mark.skipif("not config.getvalue('db')") - def test_function(...): - ... - -You can create a shortcut for your conditional skip decorator -at module level like this:: +You can of course create a shortcut for your conditional skip +decorator at module level like this:: win32only = pytest.mark.skipif("sys.platform != 'win32'") @@ -68,11 +62,10 @@ def test_function(): ... - skip all test functions of a class -------------------------------------- -As with all function :ref:`mark` you can skip test functions at the +As with all function :ref:`marking <mark>` you can skip test functions at the `whole class- or module level`_. Here is an example for skipping all methods of a test class based on the platform:: @@ -82,9 +75,10 @@ def test_function(self): "will not be setup or run under 'win32' platform" -The ``pytestmark`` decorator will be applied to each test function. -If your code targets python2.6 or above you can equivalently use -the skipif decorator on classes:: +The ``pytestmark`` special name tells py.test to apply it to each test +function in the class. If your code targets python2.6 or above you can +more naturally use the skipif decorator (and any other marker) on +classes:: @pytest.mark.skipif("sys.platform == 'win32'") class TestPosixCalls: @@ -155,6 +149,31 @@ ======================== 6 xfailed in 0.06 seconds ========================= +.. _`evaluation of skipif/xfail conditions`: + +evaluation of skipif/xfail expressions +---------------------------------------------------- + +.. versionadded:: 2.0.2 + +The evaluation of a condition string in ``pytest.mark.skipif(conditionstring)`` +or ``pytest.mark.xfail(conditionstring)`` takes place in a namespace +dictionary which is constructed as follows: + +* the namespace is initialized by putting the ``sys`` and ``os`` modules + and the pytest ``config`` object into it. + +* updated with the module globals of the test function for which the + expression is applied. + +The pytest ``config`` object allows you to skip based on a test configuration value +which you might have added:: + + @pytest.mark.skipif("not config.getvalue('db')") + def test_function(...): + ... + + imperative xfail from within a test or setup function ------------------------------------------------------ http://bitbucket.org/hpk42/pytest/changeset/02a623da9d67/ changeset: r2169:02a623da9d67 user: hpk42 date: 2011-03-05 14:16:27 summary: avoid deprecation warnings for our internal accesses affected #: 4 files (525 bytes) --- a/CHANGELOG Sat Mar 05 13:08:43 2011 +0100 +++ b/CHANGELOG Sat Mar 05 14:16:27 2011 +0100 @@ -32,6 +32,8 @@ - fixed typos in the docs (thanks Victor Garcia, Brianna Laugher) and particular thanks to Laura Creighton who also revieved parts of the documentation. +- more precise (avoiding of) deprecation warnings for node.Class|Function accesses + Changes between 2.0.0 and 2.0.1 ---------------------------------------------- --- a/_pytest/main.py Sat Mar 05 13:08:43 2011 +0100 +++ b/_pytest/main.py Sat Mar 05 14:16:27 2011 +0100 @@ -121,9 +121,6 @@ def compatproperty(name): def fget(self): - #print "retrieving %r property from %s" %(name, self.fspath) - py.log._apiwarn("2.0", "use pytest.%s for " - "test collection and item classes" % name) return getattr(pytest, name) return property(fget, None, None, "deprecated attribute %r, use pytest.%s" % (name,name)) @@ -157,6 +154,14 @@ File = compatproperty("File") Item = compatproperty("Item") + def _getcustomclass(self, name): + cls = getattr(self, name) + if cls != getattr(pytest, name): + py.log._apiwarn("2.0", "use of node.%s is deprecated, " + "use pytest_pycollect_makeitem(...) to create custom " + "collection nodes" % name) + return cls + def __repr__(self): return "<%s %r>" %(self.__class__.__name__, getattr(self, 'name', None)) --- a/_pytest/python.py Sat Mar 05 13:08:43 2011 +0100 +++ b/_pytest/python.py Sat Mar 05 14:16:27 2011 +0100 @@ -73,7 +73,8 @@ if collector._istestclasscandidate(name, obj): #if hasattr(collector.obj, 'unittest'): # return # we assume it's a mixin class for a TestCase derived one - return collector.Class(name, parent=collector) + Class = collector._getcustomclass("Class") + return Class(name, parent=collector) elif collector.funcnamefilter(name) and hasattr(obj, '__call__'): if is_generator(obj): return Generator(name, parent=collector) @@ -213,16 +214,18 @@ extra.append(cls()) plugins = self.getplugins() + extra gentesthook.pcall(plugins, metafunc=metafunc) + Function = self._getcustomclass("Function") if not metafunc._calls: - return self.Function(name, parent=self) + return Function(name, parent=self) l = [] for callspec in metafunc._calls: subname = "%s[%s]" %(name, callspec.id) - function = self.Function(name=subname, parent=self, + function = Function(name=subname, parent=self, callspec=callspec, callobj=funcobj, keywords={callspec.id:True}) l.append(function) return l + class Module(pytest.File, PyCollectorMixin): def _getobj(self): return self._memoizedcall('_obj', self._importtestmodule) @@ -272,7 +275,7 @@ class Class(PyCollectorMixin, pytest.Collector): def collect(self): - return [self.Instance(name="()", parent=self)] + return [self._getcustomclass("Instance")(name="()", parent=self)] def setup(self): setup_class = getattr(self.obj, 'setup_class', None) --- a/testing/test_collection.py Sat Mar 05 13:08:43 2011 +0100 +++ b/testing/test_collection.py Sat Mar 05 14:16:27 2011 +0100 @@ -15,15 +15,10 @@ """) recwarn.clear() assert modcol.Module == pytest.Module - recwarn.pop(DeprecationWarning) assert modcol.Class == pytest.Class - recwarn.pop(DeprecationWarning) assert modcol.Item == pytest.Item - recwarn.pop(DeprecationWarning) assert modcol.File == pytest.File - recwarn.pop(DeprecationWarning) assert modcol.Function == pytest.Function - recwarn.pop(DeprecationWarning) def test_check_equality(self, testdir): modcol = testdir.getmodulecol(""" Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. _______________________________________________ py-svn mailing list py-svn@codespeak.net http://codespeak.net/mailman/listinfo/py-svn