unittest.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. """ discovery and running of std-library "unittest" style tests. """
  2. from __future__ import absolute_import, division, print_function
  3. import sys
  4. import traceback
  5. # for transferring markers
  6. import _pytest._code
  7. from _pytest.config import hookimpl
  8. from _pytest.outcomes import fail, skip, xfail
  9. from _pytest.python import transfer_markers, Class, Module, Function
  10. def pytest_pycollect_makeitem(collector, name, obj):
  11. # has unittest been imported and is obj a subclass of its TestCase?
  12. try:
  13. if not issubclass(obj, sys.modules["unittest"].TestCase):
  14. return
  15. except Exception:
  16. return
  17. # yes, so let's collect it
  18. return UnitTestCase(name, parent=collector)
  19. class UnitTestCase(Class):
  20. # marker for fixturemanger.getfixtureinfo()
  21. # to declare that our children do not support funcargs
  22. nofuncargs = True
  23. def setup(self):
  24. cls = self.obj
  25. if getattr(cls, "__unittest_skip__", False):
  26. return # skipped
  27. setup = getattr(cls, "setUpClass", None)
  28. if setup is not None:
  29. setup()
  30. teardown = getattr(cls, "tearDownClass", None)
  31. if teardown is not None:
  32. self.addfinalizer(teardown)
  33. super(UnitTestCase, self).setup()
  34. def collect(self):
  35. from unittest import TestLoader
  36. cls = self.obj
  37. if not getattr(cls, "__test__", True):
  38. return
  39. self.session._fixturemanager.parsefactories(self, unittest=True)
  40. loader = TestLoader()
  41. module = self.getparent(Module).obj
  42. foundsomething = False
  43. for name in loader.getTestCaseNames(self.obj):
  44. x = getattr(self.obj, name)
  45. if not getattr(x, "__test__", True):
  46. continue
  47. funcobj = getattr(x, "im_func", x)
  48. transfer_markers(funcobj, cls, module)
  49. yield TestCaseFunction(name, parent=self, callobj=funcobj)
  50. foundsomething = True
  51. if not foundsomething:
  52. runtest = getattr(self.obj, "runTest", None)
  53. if runtest is not None:
  54. ut = sys.modules.get("twisted.trial.unittest", None)
  55. if ut is None or runtest != ut.TestCase.runTest:
  56. yield TestCaseFunction("runTest", parent=self)
  57. class TestCaseFunction(Function):
  58. nofuncargs = True
  59. _excinfo = None
  60. _testcase = None
  61. def setup(self):
  62. self._testcase = self.parent.obj(self.name)
  63. self._fix_unittest_skip_decorator()
  64. self._obj = getattr(self._testcase, self.name)
  65. if hasattr(self._testcase, "setup_method"):
  66. self._testcase.setup_method(self._obj)
  67. if hasattr(self, "_request"):
  68. self._request._fillfixtures()
  69. def _fix_unittest_skip_decorator(self):
  70. """
  71. The @unittest.skip decorator calls functools.wraps(self._testcase)
  72. The call to functools.wraps() fails unless self._testcase
  73. has a __name__ attribute. This is usually automatically supplied
  74. if the test is a function or method, but we need to add manually
  75. here.
  76. See issue #1169
  77. """
  78. if sys.version_info[0] == 2:
  79. setattr(self._testcase, "__name__", self.name)
  80. def teardown(self):
  81. if hasattr(self._testcase, "teardown_method"):
  82. self._testcase.teardown_method(self._obj)
  83. # Allow garbage collection on TestCase instance attributes.
  84. self._testcase = None
  85. self._obj = None
  86. def startTest(self, testcase):
  87. pass
  88. def _addexcinfo(self, rawexcinfo):
  89. # unwrap potential exception info (see twisted trial support below)
  90. rawexcinfo = getattr(rawexcinfo, "_rawexcinfo", rawexcinfo)
  91. try:
  92. excinfo = _pytest._code.ExceptionInfo(rawexcinfo)
  93. except TypeError:
  94. try:
  95. try:
  96. values = traceback.format_exception(*rawexcinfo)
  97. values.insert(
  98. 0,
  99. "NOTE: Incompatible Exception Representation, "
  100. "displaying natively:\n\n",
  101. )
  102. fail("".join(values), pytrace=False)
  103. except (fail.Exception, KeyboardInterrupt):
  104. raise
  105. except: # noqa
  106. fail(
  107. "ERROR: Unknown Incompatible Exception "
  108. "representation:\n%r" % (rawexcinfo,),
  109. pytrace=False,
  110. )
  111. except KeyboardInterrupt:
  112. raise
  113. except fail.Exception:
  114. excinfo = _pytest._code.ExceptionInfo()
  115. self.__dict__.setdefault("_excinfo", []).append(excinfo)
  116. def addError(self, testcase, rawexcinfo):
  117. self._addexcinfo(rawexcinfo)
  118. def addFailure(self, testcase, rawexcinfo):
  119. self._addexcinfo(rawexcinfo)
  120. def addSkip(self, testcase, reason):
  121. try:
  122. skip(reason)
  123. except skip.Exception:
  124. self._skipped_by_mark = True
  125. self._addexcinfo(sys.exc_info())
  126. def addExpectedFailure(self, testcase, rawexcinfo, reason=""):
  127. try:
  128. xfail(str(reason))
  129. except xfail.Exception:
  130. self._addexcinfo(sys.exc_info())
  131. def addUnexpectedSuccess(self, testcase, reason=""):
  132. self._unexpectedsuccess = reason
  133. def addSuccess(self, testcase):
  134. pass
  135. def stopTest(self, testcase):
  136. pass
  137. def _handle_skip(self):
  138. # implements the skipping machinery (see #2137)
  139. # analog to pythons Lib/unittest/case.py:run
  140. testMethod = getattr(self._testcase, self._testcase._testMethodName)
  141. if getattr(self._testcase.__class__, "__unittest_skip__", False) or getattr(
  142. testMethod, "__unittest_skip__", False
  143. ):
  144. # If the class or method was skipped.
  145. skip_why = getattr(
  146. self._testcase.__class__, "__unittest_skip_why__", ""
  147. ) or getattr(testMethod, "__unittest_skip_why__", "")
  148. try: # PY3, unittest2 on PY2
  149. self._testcase._addSkip(self, self._testcase, skip_why)
  150. except TypeError: # PY2
  151. if sys.version_info[0] != 2:
  152. raise
  153. self._testcase._addSkip(self, skip_why)
  154. return True
  155. return False
  156. def runtest(self):
  157. if self.config.pluginmanager.get_plugin("pdbinvoke") is None:
  158. self._testcase(result=self)
  159. else:
  160. # disables tearDown and cleanups for post mortem debugging (see #1890)
  161. if self._handle_skip():
  162. return
  163. self._testcase.debug()
  164. def _prunetraceback(self, excinfo):
  165. Function._prunetraceback(self, excinfo)
  166. traceback = excinfo.traceback.filter(
  167. lambda x: not x.frame.f_globals.get("__unittest")
  168. )
  169. if traceback:
  170. excinfo.traceback = traceback
  171. @hookimpl(tryfirst=True)
  172. def pytest_runtest_makereport(item, call):
  173. if isinstance(item, TestCaseFunction):
  174. if item._excinfo:
  175. call.excinfo = item._excinfo.pop(0)
  176. try:
  177. del call.result
  178. except AttributeError:
  179. pass
  180. # twisted trial support
  181. @hookimpl(hookwrapper=True)
  182. def pytest_runtest_protocol(item):
  183. if isinstance(item, TestCaseFunction) and "twisted.trial.unittest" in sys.modules:
  184. ut = sys.modules["twisted.python.failure"]
  185. Failure__init__ = ut.Failure.__init__
  186. check_testcase_implements_trial_reporter()
  187. def excstore(
  188. self, exc_value=None, exc_type=None, exc_tb=None, captureVars=None
  189. ):
  190. if exc_value is None:
  191. self._rawexcinfo = sys.exc_info()
  192. else:
  193. if exc_type is None:
  194. exc_type = type(exc_value)
  195. self._rawexcinfo = (exc_type, exc_value, exc_tb)
  196. try:
  197. Failure__init__(
  198. self, exc_value, exc_type, exc_tb, captureVars=captureVars
  199. )
  200. except TypeError:
  201. Failure__init__(self, exc_value, exc_type, exc_tb)
  202. ut.Failure.__init__ = excstore
  203. yield
  204. ut.Failure.__init__ = Failure__init__
  205. else:
  206. yield
  207. def check_testcase_implements_trial_reporter(done=[]):
  208. if done:
  209. return
  210. from zope.interface import classImplements
  211. from twisted.trial.itrial import IReporter
  212. classImplements(TestCaseFunction, IReporter)
  213. done.append(1)