Does anyone use @testclass ??

37 views
Skip to first unread message

Edward K. Ream

unread,
Nov 12, 2012, 7:24:25 AM11/12/12
to leo-e...@googlegroups.com
At present, the contents of an @test node is considered to be body of the runTest method of generalTestCase, a subclass of unittest.TestCase.  generalTestCase is defined in leoTest.py.

I would like to generalize this, so that the user can specify a different class, say MyTestCase, which would be a subclass of generalTestCase or TestCase.  The idea would be to define an @testclass node that would (somehow!) define a new class that could be used in other @test nodes.

I was a surprised to see that Leo already supports @testclass.  Apparently I added support for this sometime this year ;-) The body script of @testclass nodes are executed, and they are supposed to set either g.app.scriptDict['suite'] or g.app.scriptDict['testclass'].  If the latter, the present code gets the test suite to be executed as follows::

    testclass = g.app.scriptDict.get('testclass')
    suite = unittest.TestLoader().loadTestsFromTestCase(testclass)

This is all very kludgy.  Afaik, there are no @testclass nodes anywhere in Leo.

I'd like to change this to make it more flexible and useful, but first I'd like to know whether anyone is actually using the present version of @testclass.

Important: The static type checking (stc) project is the motivation for this.  At present, the unit testing code for stc uses this pattern::

    exec(g.findTestScript(c,'@common <kind of test>'))

The code in the @common <kind of test> nodes defines the setup method for what are, in effect, subclasses of generalTestCase.  This kind of Leo hack must be removed asap: we can't assume that most people who are interested in stc will use Leo!  There are two alternatives:

1. Write a script to turn references to exec(g.findTestScript,whatever) into explicit subclasses of unittest.TestCase.

2. Use another pattern, say @testclass, as the basis for the script to create explicit subclasses of unittest.TestCase.

I prefer the second approach, if possible, because it would be much more explicit about intentions.  Really, the exec hack is too ugly even for Leo users.

All comments and suggestions welcome.

Edward

Edward K. Ream

unread,
Nov 12, 2012, 10:13:21 AM11/12/12
to leo-e...@googlegroups.com
On Mon, Nov 12, 2012 at 6:24 AM, Edward K. Ream <edre...@gmail.com> wrote:

> This kind of Leo hack must be removed asap: we can't assume that most people who are interested in stc will use Leo!

Never mind. I'm going to leave @testclass as it is.

After I wrote the original (confused) post, I realized that stc unit
testing code can't use *any* Leo tricks, so I may as well use a
completely standard organization, namely external unit-testing files.

The following Leo script, run from statictypechecking.leo, will run
all files in the "test" directory::

import imp
import glob
import unittest

if c.isChanged():
c.save()

test_dir = g.os_path_finalize_join(
g.os_path_dirname(c.fileName()),'test')
assert g.os_path_exists(test_dir),test_dir
pattern = g.os_path_finalize_join(test_dir,'*.py')

for fn in glob.glob(pattern):
module_fn = g.shortFileName(fn[:-3])
try:
file_ = None
file_,pathname,description = imp.find_module(
module_fn,[test_dir])
module = imp.load_module(
module_fn,file_,pathname, description)
# equivalent to imp.reload
suite = unittest.TestLoader().\
loadTestsFromModule(module)
unittest.TextTestRunner(verbosity=1).run(suite)
finally:
if file_: file_.close()

No, this isn't as flexible as selecting the desired @test trees, but I
can simulate that by moving code into and out of the test directory.
Furthermore, I can use standard unittest filters to select desired
tests as well. In short, this is a reasonable start.

Edward

Edward K. Ream

unread,
Nov 12, 2012, 11:32:52 AM11/12/12
to leo-e...@googlegroups.com
On Mon, Nov 12, 2012 at 9:13 AM, Edward K. Ream <edre...@gmail.com> wrote:
> The following Leo script, run from statictypechecking.leo, will run
> all files in the "test" directory::

There is an important detail that must be handled: we must completely
kill the old version of the unit test module. This can be done as
follows::

if module_fn in sys.modules:
del(sys.modules[module_fn])

The reason is subtle: imp.load_module is equivalent to imp.reload if
the module already exists, but imp.reload does *not* clear out
previous values in the module. This will cause problems if, say, you
change the name of a test class. Without the statement above, the
unittest module will see all the previous test class, and run the
tests twice!

Edward
Reply all
Reply to author
Forward
0 new messages