[devtools] Re-implement generative tests using subtests
Generative tests as implemented in logilab.common.testib are not compatible
with tests runner other than lgc.pytest and this implementation differs from
the standard library, which has support for subtests_ since Python 3.4. Use
unittest2 to bridge the gap.
Maybe it'd be good to implement this on logilab.common.testlib side at some
point. Let's see how this gets received here first.
.. _subtests: https://docs.python.org/3/library/unittest.html#subtests
--- a/cubicweb/__pkginfo__.py Tue Jan 19 10:03:16 2016 +0100
+++ b/cubicweb/__pkginfo__.py Thu Jan 14 18:35:07 2016 +0100
@@ -52,7 +52,8 @@
'logilab-database': '>= 1.15.0',
'passlib': '',
'pytz': '',
- 'Markdown': ''
+ 'Markdown': '',
+ 'unittest2': '>= 0.7.0',
}
__recommends__ = {
--- a/cubicweb/devtools/qunit.py Tue Jan 19 10:03:16 2016 +0100
+++ b/cubicweb/devtools/qunit.py Thu Jan 14 18:35:07 2016 +0100
@@ -25,7 +25,7 @@
from six.moves.queue import Queue, Empty
# imported by default to simplify further import statements
-from logilab.common.testlib import unittest_main, with_tempdir, InnerTest, Tags
+from logilab.common.testlib import unittest_main, with_tempdir, Tags
import webtest.http
import cubicweb
@@ -109,8 +109,9 @@
depends = args[1]
else:
depends = ()
- for js_test in self._test_qunit(test_file, depends):
- yield js_test
+ for name, func, args in self._test_qunit(test_file, depends):
+ with self.subTest(name=name):
+ func(*args)
@with_tempdir
def _test_qunit(self, test_file, depends=(), timeout=10):
@@ -127,8 +128,10 @@
browser.start(self.config['base-url'] + "?vid=qunit")
test_count = 0
error = False
- def raise_exception(cls, *data):
- raise cls(*data)
+
+ def runtime_error(*data):
+ raise RuntimeError(*data)
+
while not error:
try:
result, test_name, msg = self.test_queue.get(timeout=timeout)
@@ -138,18 +141,16 @@
break
test_count += 1
if result:
- yield InnerTest(test_name, lambda : 1)
+ yield test_name, lambda *args: 1, ()
else:
- yield InnerTest(test_name, self.fail, msg)
+ yield test_name, self.fail, (msg, )
except Empty:
error = True
msg = '%s inactivity timeout (%is). %i test results received'
- yield InnerTest(test_file, raise_exception, RuntimeError,
- msg % (test_file, timeout, test_count))
+ yield test_file, runtime_error, (msg % (test_file, timeout, test_count), )
browser.stop()
if test_count <= 0 and not error:
- yield InnerTest(test_name, raise_exception, RuntimeError,
- 'No test yielded by qunit for %s' % test_file)
+ yield test_name, runtime_error, ('No test yielded by qunit for %s' % test_file, )
class QUnitResultController(Controller):
--- a/cubicweb/devtools/repotest.py Tue Jan 19 10:03:16 2016 +0100
+++ b/cubicweb/devtools/repotest.py Thu Jan 14 18:35:07 2016 +0100
@@ -137,13 +137,14 @@
from rql import RQLHelper
+from cubicweb.devtools.testlib import BaseTestCase
from cubicweb.devtools.fake import FakeRepo, FakeConfig, FakeSession
from cubicweb.server import set_debug, debugged
from cubicweb.server.querier import QuerierHelper
from cubicweb.server.session import Session
from cubicweb.server.sources.rql2sql import SQLGenerator, remove_unused_solutions
-class RQLGeneratorTC(TestCase):
+class RQLGeneratorTC(BaseTestCase):
schema = backend = None # set this in concrete class
@classmethod
--- a/cubicweb/devtools/test/unittest_qunit.py Tue Jan 19 10:03:16 2016 +0100
+++ b/cubicweb/devtools/test/unittest_qunit.py Thu Jan 14 18:35:07 2016 +0100
@@ -17,9 +17,9 @@
js_tests = list(self._test_qunit(js('test_simple_failure.js')))
self.assertEqual(len(js_tests), 3)
test_1, test_2, test_3 = js_tests
- self.assertRaises(self.failureException, test_1[0], *test_1[1:])
- self.assertRaises(self.failureException, test_2[0], *test_2[1:])
- test_3[0](*test_3[1:])
+ self.assertRaises(self.failureException, test_1[1], *test_1[2:])
+ self.assertRaises(self.failureException, test_2[1], *test_2[2:])
+ test_3[1](*test_3[2:])
if __name__ == '__main__':
--- a/cubicweb/devtools/testlib.py Tue Jan 19 10:03:16 2016 +0100
+++ b/cubicweb/devtools/testlib.py Thu Jan 14 18:35:07 2016 +0100
@@ -31,7 +31,7 @@
import yams.schema
-from logilab.common.testlib import TestCase, InnerTest, Tags
+from logilab.common.testlib import TestCase, Tags
from logilab.common.pytest import nocoverage
from logilab.common.debugger import Debugger
from logilab.common.umessage import message_from_string
@@ -51,6 +51,16 @@
from cubicweb.devtools import fake, htmlparser, DEFAULT_EMPTY_DB_ID
+if sys.version_info[:2] < (3, 4):
+ import unittest2
+ if not hasattr(unittest2.TestCase, 'subTest'):
+ raise ImportError('no subTest support in available unittest2')
+ class BaseTestCase(unittest2.TestCase, TestCase):
+ """Mix of logilab.common.testlib.TestCase and unittest2.TestCase"""
+else:
+ BaseTestCase = TestCase
+
+
# low-level utilities ##########################################################
class CubicWebDebugger(Debugger):
@@ -253,7 +263,7 @@
# base class for cubicweb tests requiring a full cw environments ###############
-class CubicWebTC(TestCase):
+class CubicWebTC(BaseTestCase):
"""abstract class for test using an apptest environment
attributes:
@@ -1238,18 +1248,19 @@
propdefs[k]['default'] = True
for view in self.list_views_for(rset):
backup_rset = rset.copy(rset.rows, rset.description)
- yield InnerTest(self._testname(rset, view.__regid__, 'view'),
- self.view, view.__regid__, rset,
- rset.req.reset_headers(), 'main-template')
+ with self.subTest(name=self._testname(rset, view.__regid__, 'view')):
+ self.view(view.__regid__, rset,
+ rset.req.reset_headers(), 'main-template')
# We have to do this because some views modify the
# resultset's syntax tree
rset = backup_rset
for action in self.list_actions_for(rset):
- yield InnerTest(self._testname(rset, action.__regid__, 'action'),
- self._test_action, action)
+ with self.subTest(name=self._testname(rset, action.__regid__, 'action')):
+ self._test_action(action)
for box in self.list_boxes_for(rset):
w = [].append
- yield InnerTest(self._testname(rset, box.__regid__, 'box'), box.render, w)
+ with self.subTest(self._testname(rset, box.__regid__, 'box')):
+ box.render(w)
@staticmethod
def _testname(rset, objid, objtype):
@@ -1277,19 +1288,18 @@
def test_one_each_config(self):
self.auto_populate(1)
for rset in self.iter_automatic_rsets(limit=1):
- for testargs in self._test_everything_for(rset):
- yield testargs
+ self._test_everything_for(rset)
def test_ten_each_config(self):
self.auto_populate(10)
for rset in self.iter_automatic_rsets(limit=10):
- for testargs in self._test_everything_for(rset):
- yield testargs
+ self._test_everything_for(rset)
def test_startup_views(self):
for vid in self.list_startup_views():
with self.admin_access.web_request() as req:
- yield self.view, vid, None, req
+ with self.subTest(vid=vid):
+ self.view(vid, None, req)
# registry instrumentization ###################################################
--- a/debian/control Tue Jan 19 10:03:16 2016 +0100
+++ b/debian/control Thu Jan 14 18:35:07 2016 +0100
@@ -11,7 +11,7 @@
python-six (>= 1.4.0),
python-sphinx,
python-logilab-common,
- python-unittest2 | python (>= 2.7),
+ python-unittest2 (>= 0.7.0) | python (>= 3.4),
python-logilab-mtconverter,
python-markdown,
python-tz,