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,10 +1001,14 @@ class FuncargRequest:
|
||||||
if clscol:
|
if clscol:
|
||||||
return clscol.obj
|
return clscol.obj
|
||||||
|
|
||||||
@scopeproperty()
|
@property
|
||||||
def instance(self):
|
def instance(self):
|
||||||
""" instance (can be None) on which test function was collected. """
|
""" instance (can be None) on which test function was collected. """
|
||||||
return py.builtin._getimself(self.function)
|
# unittest support hack, see _pytest.unittest.TestCaseFunction
|
||||||
|
try:
|
||||||
|
return self._pyfuncitem._testcase
|
||||||
|
except AttributeError:
|
||||||
|
return py.builtin._getimself(self.function)
|
||||||
|
|
||||||
@scopeproperty()
|
@scopeproperty()
|
||||||
def module(self):
|
def module(self):
|
||||||
|
@ -1341,7 +1345,7 @@ class FuncargManager:
|
||||||
for fin in l:
|
for fin in l:
|
||||||
fin()
|
fin()
|
||||||
|
|
||||||
def _parsefactories(self, holderobj, nodeid):
|
def _parsefactories(self, holderobj, nodeid, unittest=False):
|
||||||
if holderobj in self._holderobjseen:
|
if holderobj in self._holderobjseen:
|
||||||
return
|
return
|
||||||
#print "parsefactories", holderobj
|
#print "parsefactories", holderobj
|
||||||
|
@ -1372,7 +1376,7 @@ class FuncargManager:
|
||||||
setup = getattr(obj, "_pytestsetup", None)
|
setup = getattr(obj, "_pytestsetup", None)
|
||||||
if setup is not None:
|
if setup is not None:
|
||||||
scope = setup.scope
|
scope = setup.scope
|
||||||
sf = SetupCall(self, nodeid, obj, scope)
|
sf = SetupCall(self, nodeid, obj, scope, unittest)
|
||||||
self.setuplist.append(sf)
|
self.setuplist.append(sf)
|
||||||
continue
|
continue
|
||||||
faclist = self.arg2facspec.setdefault(argname, [])
|
faclist = self.arg2facspec.setdefault(argname, [])
|
||||||
|
@ -1428,7 +1432,6 @@ class FuncargManager:
|
||||||
request._factorystack.append(setupcall)
|
request._factorystack.append(setupcall)
|
||||||
mp = monkeypatch()
|
mp = monkeypatch()
|
||||||
try:
|
try:
|
||||||
#mp.setattr(request, "_setupcall", setupcall, raising=False)
|
|
||||||
mp.setattr(request, "scope", setupcall.scope)
|
mp.setattr(request, "scope", setupcall.scope)
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
for name in setupcall.funcargnames:
|
for name in setupcall.funcargnames:
|
||||||
|
@ -1438,7 +1441,12 @@ class FuncargManager:
|
||||||
self.session._setupstate.addfinalizer(setupcall.finish, scol)
|
self.session._setupstate.addfinalizer(setupcall.finish, scol)
|
||||||
for argname in setupcall.funcargnames: # XXX all deps?
|
for argname in setupcall.funcargnames: # XXX all deps?
|
||||||
self.addargfinalizer(setupcall.finish, argname)
|
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:
|
finally:
|
||||||
mp.undo()
|
mp.undo()
|
||||||
request._factorystack.remove(setupcall)
|
request._factorystack.remove(setupcall)
|
||||||
|
@ -1457,20 +1465,21 @@ class FuncargManager:
|
||||||
|
|
||||||
class SetupCall:
|
class SetupCall:
|
||||||
""" a container/helper for managing calls to setup functions. """
|
""" 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.funcargmanager = funcargmanager
|
||||||
self.baseid = baseid
|
self.baseid = baseid
|
||||||
self.func = func
|
self.func = func
|
||||||
self.funcargnames = getfuncargnames(func)
|
self.funcargnames = getfuncargnames(func, startindex=int(unittest))
|
||||||
self.scope = scope
|
self.scope = scope
|
||||||
self.scopenum = scopes.index(scope)
|
self.scopenum = scopes.index(scope)
|
||||||
self.active = False
|
self.active = False
|
||||||
|
self.unittest= unittest
|
||||||
self._finalizer = []
|
self._finalizer = []
|
||||||
|
|
||||||
def execute(self, kwargs):
|
def execute(self, posargs, kwargs):
|
||||||
assert not self.active
|
assert not self.active
|
||||||
self.active = True
|
self.active = True
|
||||||
self.func(**kwargs)
|
self.func(*posargs, **kwargs)
|
||||||
|
|
||||||
def addfinalizer(self, finalizer):
|
def addfinalizer(self, finalizer):
|
||||||
assert self.active
|
assert self.active
|
||||||
|
|
|
@ -21,6 +21,8 @@ def pytest_pycollect_makeitem(collector, name, obj):
|
||||||
|
|
||||||
class UnitTestCase(pytest.Class):
|
class UnitTestCase(pytest.Class):
|
||||||
def collect(self):
|
def collect(self):
|
||||||
|
self.session.funcargmanager._parsefactories(self.obj, self.nodeid,
|
||||||
|
unittest=True)
|
||||||
loader = py.std.unittest.TestLoader()
|
loader = py.std.unittest.TestLoader()
|
||||||
module = self.getparent(pytest.Module).obj
|
module = self.getparent(pytest.Module).obj
|
||||||
cls = self.obj
|
cls = self.obj
|
||||||
|
@ -56,6 +58,8 @@ class TestCaseFunction(pytest.Function):
|
||||||
pytest.skip(self._obj.skip)
|
pytest.skip(self._obj.skip)
|
||||||
if hasattr(self._testcase, 'setup_method'):
|
if hasattr(self._testcase, 'setup_method'):
|
||||||
self._testcase.setup_method(self._obj)
|
self._testcase.setup_method(self._obj)
|
||||||
|
if hasattr(self, "_request"):
|
||||||
|
self._request._callsetup()
|
||||||
|
|
||||||
def teardown(self):
|
def teardown(self):
|
||||||
if hasattr(self._testcase, 'teardown_method'):
|
if hasattr(self._testcase, 'teardown_method'):
|
||||||
|
|
|
@ -24,8 +24,8 @@ Running it yields::
|
||||||
|
|
||||||
$ py.test test_unittest.py
|
$ py.test test_unittest.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev12
|
||||||
plugins: xdist, bugzilla, cache, oejskit, pep8, cov
|
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
||||||
collecting ... collected 1 items
|
collecting ... collected 1 items
|
||||||
|
|
||||||
test_unittest.py F
|
test_unittest.py F
|
||||||
|
@ -47,3 +47,64 @@ Running it yields::
|
||||||
|
|
||||||
.. _`unittest.py style`: http://docs.python.org/library/unittest.html
|
.. _`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',
|
name='pytest',
|
||||||
description='py.test: simple powerful testing with Python',
|
description='py.test: simple powerful testing with Python',
|
||||||
long_description = long_description,
|
long_description = long_description,
|
||||||
version='2.3.0.dev12',
|
version='2.3.0.dev13',
|
||||||
url='http://pytest.org',
|
url='http://pytest.org',
|
||||||
license='MIT license',
|
license='MIT license',
|
||||||
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
|
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
|
||||||
|
|
|
@ -1792,8 +1792,7 @@ class TestFuncargManager:
|
||||||
reprec.assertoutcome(passed=1)
|
reprec.assertoutcome(passed=1)
|
||||||
|
|
||||||
class TestSetupDiscovery:
|
class TestSetupDiscovery:
|
||||||
def pytest_funcarg__testdir(self, request):
|
def pytest_funcarg__testdir(self, testdir):
|
||||||
testdir = request.getfuncargvalue("testdir")
|
|
||||||
testdir.makeconftest("""
|
testdir.makeconftest("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.setup()
|
@pytest.setup()
|
||||||
|
@ -1832,6 +1831,21 @@ class TestSetupDiscovery:
|
||||||
reprec = testdir.inline_run("-s")
|
reprec = testdir.inline_run("-s")
|
||||||
reprec.assertoutcome(passed=1)
|
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):
|
def test_callables_nocode(self, testdir):
|
||||||
"""
|
"""
|
||||||
a imported mock.call would break setup/factory discovery
|
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