implement full @pytest.setup function unittest.TestCase interaction
This commit is contained in:
parent
d9c24552fc
commit
a7c6688bd6
|
@ -1,2 +1,2 @@
|
|||
#
|
||||
__version__ = '2.3.0.dev12'
|
||||
__version__ = '2.3.0.dev13'
|
||||
|
|
|
@ -1001,9 +1001,13 @@ class FuncargRequest:
|
|||
if clscol:
|
||||
return clscol.obj
|
||||
|
||||
@scopeproperty()
|
||||
@property
|
||||
def instance(self):
|
||||
""" instance (can be None) on which test function was collected. """
|
||||
# unittest support hack, see _pytest.unittest.TestCaseFunction
|
||||
try:
|
||||
return self._pyfuncitem._testcase
|
||||
except AttributeError:
|
||||
return py.builtin._getimself(self.function)
|
||||
|
||||
@scopeproperty()
|
||||
|
@ -1341,7 +1345,7 @@ class FuncargManager:
|
|||
for fin in l:
|
||||
fin()
|
||||
|
||||
def _parsefactories(self, holderobj, nodeid):
|
||||
def _parsefactories(self, holderobj, nodeid, unittest=False):
|
||||
if holderobj in self._holderobjseen:
|
||||
return
|
||||
#print "parsefactories", holderobj
|
||||
|
@ -1372,7 +1376,7 @@ class FuncargManager:
|
|||
setup = getattr(obj, "_pytestsetup", None)
|
||||
if setup is not None:
|
||||
scope = setup.scope
|
||||
sf = SetupCall(self, nodeid, obj, scope)
|
||||
sf = SetupCall(self, nodeid, obj, scope, unittest)
|
||||
self.setuplist.append(sf)
|
||||
continue
|
||||
faclist = self.arg2facspec.setdefault(argname, [])
|
||||
|
@ -1428,7 +1432,6 @@ class FuncargManager:
|
|||
request._factorystack.append(setupcall)
|
||||
mp = monkeypatch()
|
||||
try:
|
||||
#mp.setattr(request, "_setupcall", setupcall, raising=False)
|
||||
mp.setattr(request, "scope", setupcall.scope)
|
||||
kwargs = {}
|
||||
for name in setupcall.funcargnames:
|
||||
|
@ -1438,7 +1441,12 @@ class FuncargManager:
|
|||
self.session._setupstate.addfinalizer(setupcall.finish, scol)
|
||||
for argname in setupcall.funcargnames: # XXX all deps?
|
||||
self.addargfinalizer(setupcall.finish, argname)
|
||||
setupcall.execute(kwargs)
|
||||
# for unittest-setup methods we need to provide
|
||||
# the correct instance
|
||||
posargs = ()
|
||||
if setupcall.unittest:
|
||||
posargs = (request.instance,)
|
||||
setupcall.execute(posargs, kwargs)
|
||||
finally:
|
||||
mp.undo()
|
||||
request._factorystack.remove(setupcall)
|
||||
|
@ -1457,20 +1465,21 @@ class FuncargManager:
|
|||
|
||||
class SetupCall:
|
||||
""" a container/helper for managing calls to setup functions. """
|
||||
def __init__(self, funcargmanager, baseid, func, scope):
|
||||
def __init__(self, funcargmanager, baseid, func, scope, unittest):
|
||||
self.funcargmanager = funcargmanager
|
||||
self.baseid = baseid
|
||||
self.func = func
|
||||
self.funcargnames = getfuncargnames(func)
|
||||
self.funcargnames = getfuncargnames(func, startindex=int(unittest))
|
||||
self.scope = scope
|
||||
self.scopenum = scopes.index(scope)
|
||||
self.active = False
|
||||
self.unittest= unittest
|
||||
self._finalizer = []
|
||||
|
||||
def execute(self, kwargs):
|
||||
def execute(self, posargs, kwargs):
|
||||
assert not self.active
|
||||
self.active = True
|
||||
self.func(**kwargs)
|
||||
self.func(*posargs, **kwargs)
|
||||
|
||||
def addfinalizer(self, finalizer):
|
||||
assert self.active
|
||||
|
|
|
@ -21,6 +21,8 @@ def pytest_pycollect_makeitem(collector, name, obj):
|
|||
|
||||
class UnitTestCase(pytest.Class):
|
||||
def collect(self):
|
||||
self.session.funcargmanager._parsefactories(self.obj, self.nodeid,
|
||||
unittest=True)
|
||||
loader = py.std.unittest.TestLoader()
|
||||
module = self.getparent(pytest.Module).obj
|
||||
cls = self.obj
|
||||
|
@ -56,6 +58,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._callsetup()
|
||||
|
||||
def teardown(self):
|
||||
if hasattr(self._testcase, 'teardown_method'):
|
||||
|
|
|
@ -24,8 +24,8 @@ Running it yields::
|
|||
|
||||
$ py.test test_unittest.py
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2
|
||||
plugins: xdist, bugzilla, cache, oejskit, pep8, cov
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev12
|
||||
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
||||
collecting ... collected 1 items
|
||||
|
||||
test_unittest.py F
|
||||
|
@ -47,3 +47,64 @@ Running it yields::
|
|||
|
||||
.. _`unittest.py style`: http://docs.python.org/library/unittest.html
|
||||
|
||||
Moreover, you can use the new :ref:`@pytest.setup functions <@pytest.setup>`
|
||||
functions and make use of pytest's unique :ref:`funcarg mechanism` in your
|
||||
test suite::
|
||||
|
||||
# content of test_unittest_funcargs.py
|
||||
import pytest
|
||||
import unittest
|
||||
|
||||
class MyTest(unittest.TestCase):
|
||||
@pytest.setup()
|
||||
def chdir(self, tmpdir):
|
||||
tmpdir.chdir() # change to pytest-provided temporary directory
|
||||
tmpdir.join("samplefile.ini").write("# testdata")
|
||||
|
||||
def test_method(self):
|
||||
s = open("samplefile.ini").read()
|
||||
assert "testdata" in s
|
||||
|
||||
Running this file should give us one passed test because the setup
|
||||
function took care to prepare a directory with some test data
|
||||
which the unittest-testcase method can now use::
|
||||
|
||||
$ py.test -q test_unittest_funcargs.py
|
||||
collecting ... collected 1 items
|
||||
.
|
||||
1 passed in 0.28 seconds
|
||||
|
||||
If you want to make a database attribute available on unittest.TestCases
|
||||
instances, based on a marker, you can do it using :ref:`pytest.mark`` and
|
||||
:ref:`setup functions`::
|
||||
|
||||
# content of test_unittest_marked_db.py
|
||||
import pytest
|
||||
import unittest
|
||||
|
||||
@pytest.factory()
|
||||
def db():
|
||||
class DummyDB:
|
||||
x = 1
|
||||
return DummyDB()
|
||||
|
||||
@pytest.setup()
|
||||
def stick_db_to_self(request, db):
|
||||
if hasattr(request.node.markers, "needsdb"):
|
||||
request.instance.db = db
|
||||
|
||||
class MyTest(unittest.TestCase):
|
||||
def test_method(self):
|
||||
assert not hasattr(self, "db")
|
||||
|
||||
@pytest.mark.needsdb
|
||||
def test_method2(self):
|
||||
assert self.db.x == 1
|
||||
|
||||
Running it passes both tests, one of which will see a ``db`` attribute
|
||||
because of the according ``needsdb`` marker::
|
||||
|
||||
$ py.test -q test_unittest_marked_db.py
|
||||
collecting ... collected 2 items
|
||||
..
|
||||
2 passed in 0.03 seconds
|
||||
|
|
2
setup.py
2
setup.py
|
@ -24,7 +24,7 @@ def main():
|
|||
name='pytest',
|
||||
description='py.test: simple powerful testing with Python',
|
||||
long_description = long_description,
|
||||
version='2.3.0.dev12',
|
||||
version='2.3.0.dev13',
|
||||
url='http://pytest.org',
|
||||
license='MIT license',
|
||||
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
|
||||
|
|
|
@ -1792,8 +1792,7 @@ class TestFuncargManager:
|
|||
reprec.assertoutcome(passed=1)
|
||||
|
||||
class TestSetupDiscovery:
|
||||
def pytest_funcarg__testdir(self, request):
|
||||
testdir = request.getfuncargvalue("testdir")
|
||||
def pytest_funcarg__testdir(self, testdir):
|
||||
testdir.makeconftest("""
|
||||
import pytest
|
||||
@pytest.setup()
|
||||
|
@ -1832,6 +1831,21 @@ class TestSetupDiscovery:
|
|||
reprec = testdir.inline_run("-s")
|
||||
reprec.assertoutcome(passed=1)
|
||||
|
||||
def test_setup_at_classlevel(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
import pytest
|
||||
class TestClass:
|
||||
@pytest.setup()
|
||||
def permethod(self, request):
|
||||
request.instance.funcname = request.function.__name__
|
||||
def test_method1(self):
|
||||
assert self.funcname == "test_method1"
|
||||
def test_method2(self):
|
||||
assert self.funcname == "test_method2"
|
||||
""")
|
||||
reprec = testdir.inline_run("-s")
|
||||
reprec.assertoutcome(passed=2)
|
||||
|
||||
def test_callables_nocode(self, testdir):
|
||||
"""
|
||||
a imported mock.call would break setup/factory discovery
|
||||
|
|
|
@ -480,3 +480,28 @@ def test_unittest_unexpected_failure(testdir):
|
|||
])
|
||||
|
||||
|
||||
|
||||
def test_unittest_setup_interaction(testdir):
|
||||
testdir.makepyfile("""
|
||||
import unittest
|
||||
import pytest
|
||||
class MyTestCase(unittest.TestCase):
|
||||
@pytest.setup(scope="class")
|
||||
def perclass(self, request):
|
||||
request.cls.hello = "world"
|
||||
@pytest.setup(scope="function")
|
||||
def perfunction(self, request):
|
||||
request.instance.funcname = request.function.__name__
|
||||
|
||||
def test_method1(self):
|
||||
assert self.funcname == "test_method1"
|
||||
assert self.hello == "world"
|
||||
|
||||
def test_method2(self):
|
||||
assert self.funcname == "test_method2"
|
||||
|
||||
def test_classattr(self):
|
||||
assert self.__class__.hello == "world"
|
||||
""")
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines("*3 passed*")
|
||||
|
|
Loading…
Reference in New Issue