[Twisted-Python] Remove setupClass branch of trial - what is the motivation?

Jonathan Lange jml at mumak.net
Wed Nov 2 22:46:15 MST 2005


On 02/11/05, glyph at divmod.com <glyph at divmod.com> wrote:
>
>
> On Tue, 1 Nov 2005 12:38:21 -0500, Jean-Paul Calderone <exarkun at divmod.com> wrote:
> >On Tue, 01 Nov 2005 10:47:09 -0500, Itamar Shtull-Trauring
> ><itamar at itamarst.org> wrote:
> >>Hi,
> >>
> >>Still haven't gotten an answer to this; why is the motivation for
> >>deprecating a perfectly reasonable feature?
> >
> >FWIW, I also would like to know the answer to this question.
>
> Yeah, uh...
>
> As I understand it the implementation was previously horrendously broken.  Now it is not.  Given that it is now no longer broken, and some quite reasonable tests I've seen make use of it (in particular: tests for Divmod ClickChronicle do some very expensive setup in a setUpClass), I'd like to see it stick around.
>

Thanks everyone for your patience while I make the time to answer this question.

I'd like to deprecate setUpClass, but I'm not totally sold on it.  The
biggest problem I have with it is that it encourages (nay, forces)
users to share state between tests.  This may be a problem inherent
with any setup optimisation -- but I'd like to experiment first.

My other problems are to do with implementation -- and these are the
problems I feel most keenly.  Trial only knows about classes in the
test loader.  After that, it only knows about things to call run() on.
 Sometimes these are collections of tests (suites), sometimes these
are actual tests.  setUpClass mandates a ClassSuite -- a suite that
runs setUpClass at the start and tearDownClass at the end.

One big implication is that a TestCase isn't (necessarily) a
self-contained TestCase.  It has to be wrapped in a ClassSuite to make
sure that it gets properly set up and torn down.  Running a single
test shouldn't require a suite.

ClassSuite also means that the test loader needs to have two suite
factories.  This makes it more difficult to extend (which you'd want
to do for alternative runners (ala GUIs) and also if you have a cool
way to optimise your tests locally -- see below).

In addition, ClassSuite makes "what happens when tests are run" a fair
bit more complicated.  This is particularly confusing wrt
KeyboardInterrupt and reactor cleanup.

Oh yeah, and it makes things incompatible with unittest.


The example below shows what I'm considering at the moment.  This is
the testresources system by Robert Collins.  One of the reasons I
hesitated on this post is that I wanted to have chewed over this more.
But here 'tis.


Instead of:

class FooTest(unittest.TestCase):
    def setUpClass(self):
        self.db = ExpensiveAcquisition()

    def tearDownClass(self):
        self.db.expensiveDismissal()

    def test_foo(self):
        self.failUnless(self.db.isAwesome())

we have:

class DBResource(resources.Resource):
    def makeResource(self):
        cls.db = ExpensiveAcquisition()
        return cls.db
    makeResource = classmethod(makeResource)

    def cleanResource(self):
        cls.db.expensiveDismissal()
    cleanResource = classmethod(cleanResource)


class FooTest(unittest.ResourcedTestCase):
    resources = [ ('db', DBResource) ]

    def test_foo(self):
         self.failUnless(self.db.isAwesome())


One of the advantages of this is that it can be made faster than
setUpClass / tearDownClass because the OptimizingTestSuite can look at
all the tests and resources underneath is to establish the best order
to run the tests, and it can preserve resources between TestCase
classes.

cheers,
jml

PS. Jp, I hope your day continues as wonderfully as you thought it
would start. :)




More information about the Twisted-Python mailing list