[Twisted-Python] Writing Unittests for Twisted Applications
![](https://secure.gravatar.com/avatar/48ef90b5c7ec4c8a32fb9fb78856f53d.jpg?s=120&d=mm&r=g)
Hi all, I've been working on a project using twisted for the last few weeks. In the process I've written quite a few unittests most of which need to run a twisted reactor either directly as reactor.run or via an Application instance's run() method. It's amazing how difficult that is, given that twisted itself has an extensive set of unittests. Running only one test with a main loop is simple but running several tests that need to listen and unlisten the same ports or that have timeouts, etc. seems much more difficult than it should be. How difficult would it be to implement at least one of the following features? 1. a way to reset a reactor so that e.g. a new unit test could start with a clean state. It's OK for me if the unittest itself has to take care to do some things automatically such as disconnecting connections or unlistenting. 2. A base class (derived from unittest.TestCase or as a mix-in class) for tests that need to run the main loop. There should perhaps be two of them, one for plain reactor usage and for test with Application instances. Obviously this would require having 1. to a certain extent. However, it's OK if the class knew how to achieve a reset in as far as that is necessary for test cases. For instance, the base class could require that derived classes don't call the reactor directly to listen or to add delayed calls and go through some method instead to e.g. keep track of the ids returned by reactor.callLater. AFAICT neither is currently implemented in twisted. There even seems to be one unit test (test_sister) that's not implemented because of this. I have effectively implemented no. 2 as part of my unit-tests but only as much as I needed it so far. It didn't take much code but there are some tricky things: 1. After and unlisten when the loop has already been left, call reactor.iterate() to make sure the port is properly freed again. This was relatively easy to figure out because some twisted test cases do this. 2. Application.run() will only run the reactor mainloop if main.running is not true. Unfortunately, main.running is not unset when main.shutdown or main.crash is called, so when trying to enter application main loops more than once, even for different application instances, once has to explicitly unset main.running. My code will eventually be available under GPL as part of the 'GREAT-ER'[1] software we're developing, but it's not yet in CVS. If there's interest in including my test case base class in twisted I could make it available sooner and under LGPL. Bernhard [1] http://great-er.intevation.de/ -- Intevation GmbH http://intevation.de/ Sketch http://sketch.sourceforge.net/ MapIt! http://www.mapit.de/
![](https://secure.gravatar.com/avatar/3477a9de290ec6d77129af504faa1c0b.jpg?s=120&d=mm&r=g)
Hi Bernhard! Glad to see you're using Twisted. On 04 Dec 2002, Bernhard Herzog <bh@intevation.de> wrote:
In general, a way to reset the reactor would be cool.
If there's interest in including my test case base class in twisted I could make it available sooner and under LGPL.
FYI: Twisted patches must also be copyrighted by Glyph. Thanks, Moshe
![](https://secure.gravatar.com/avatar/48ef90b5c7ec4c8a32fb9fb78856f53d.jpg?s=120&d=mm&r=g)
I wanted to get back to this sooner, but another project and xmas+newyear intervened... Moshe Zadka <m@moshez.org> writes:
Hi Bernhard!
Hi Moshe!
Glad to see you're using Twisted.
The decision to use Twisted was easy because a) I've met at least two of the Twisted developers so I know it's in good hands :), b) I've used asyncore in a different project and after a while I was itching to do a substantial rewrite of asyncore, and c) That other project's infrastructure was evolving in the direction of twisted in some aspects
If there's interest in including my test case base class in twisted I could make it available sooner and under LGPL.
The test base class is finally publically available inder GPL for now: http://www.intevation.de/cgi-bin/viewcvs-greater.cgi/*checkout*/GREAT-ERModel/test/reactormixins.py?rev=1.1&content-type=text/plain It's actually two mix-in classes to unittest.TestCase like test cases.
FYI: Twisted patches must also be copyrighted by Glyph.
I'd have to clarify that with my employer, but that shouldn't be a problem, as we regularly contribute back to Free Software packages we use if we have something worthwhile to contribute. Bernhard -- Intevation GmbH http://intevation.de/ Sketch http://sketch.sourceforge.net/ MapIt! http://www.mapit.de/
![](https://secure.gravatar.com/avatar/48ef90b5c7ec4c8a32fb9fb78856f53d.jpg?s=120&d=mm&r=g)
I wrote: [Mixin classes for tests that use reactors]
The test base class is finally publically available inder GPL for now: http://www.intevation.de/cgi-bin/viewcvs-greater.cgi/*checkout*/GREAT-ERModel/test/reactormixins.py?rev=1.1&content-type=text/plain
Someone asked for a usage example in private mail. There are a few test cases in the same CVS repository that use the mix-ins, but more illustrative is probably a very simple example like this: import unittest from reactormixins import ReactorMixin class Test(unittest.TestCase, ReactorMixin): def setUp(self): """Extend inherited method to set later_has_been_called to 0""" self.later_has_been_called = 0 ReactorMixin.setUp(self) def test(self): """Simple test to show how to use ReactorMixin""" # scheduler self.later to be run after 0.1 seconds self.callLater(0.1, self.later) # run the reactor with a 10s time out self.run_reactor(10) self.assert_(self.later_has_been_called) def later(self): """Record that later has been called and stop the reactor""" self.later_has_been_called = 1 self.stop_reactor() if __name__ == "__main__": unittest.main() This example doesn't do any networking but should give an idea of how to use the mixin classes. The networking support in the mixins is currently limited to listening because that all I needed so far. My testcases start sub-processes that connect to the reactor in the test case. Bernhard -- Intevation GmbH http://intevation.de/ Sketch http://sketch.sourceforge.net/ MapIt! http://www.mapit.de/
![](https://secure.gravatar.com/avatar/3a7e70f3ef2ad1539da42afc85c8d09d.jpg?s=120&d=mm&r=g)
On Wed, Dec 04, 2002 at 07:14:17PM +0100, Bernhard Herzog wrote:
We'd *really* appreciate such a thing. A lot of the existing tests are so ugly that it's hard to extend them right now, and something like this would make it a lot easier to clean all of these up. Also, as moshez said, Glyph needs the copyright on contributions, but you can still retain your own copyright on it. -- Chris Armstrong << radix@twistedmatrix.com >> http://twistedmatrix.com/users/radix.twistd/
![](https://secure.gravatar.com/avatar/3477a9de290ec6d77129af504faa1c0b.jpg?s=120&d=mm&r=g)
Hi Bernhard! Glad to see you're using Twisted. On 04 Dec 2002, Bernhard Herzog <bh@intevation.de> wrote:
In general, a way to reset the reactor would be cool.
If there's interest in including my test case base class in twisted I could make it available sooner and under LGPL.
FYI: Twisted patches must also be copyrighted by Glyph. Thanks, Moshe
![](https://secure.gravatar.com/avatar/48ef90b5c7ec4c8a32fb9fb78856f53d.jpg?s=120&d=mm&r=g)
I wanted to get back to this sooner, but another project and xmas+newyear intervened... Moshe Zadka <m@moshez.org> writes:
Hi Bernhard!
Hi Moshe!
Glad to see you're using Twisted.
The decision to use Twisted was easy because a) I've met at least two of the Twisted developers so I know it's in good hands :), b) I've used asyncore in a different project and after a while I was itching to do a substantial rewrite of asyncore, and c) That other project's infrastructure was evolving in the direction of twisted in some aspects
If there's interest in including my test case base class in twisted I could make it available sooner and under LGPL.
The test base class is finally publically available inder GPL for now: http://www.intevation.de/cgi-bin/viewcvs-greater.cgi/*checkout*/GREAT-ERModel/test/reactormixins.py?rev=1.1&content-type=text/plain It's actually two mix-in classes to unittest.TestCase like test cases.
FYI: Twisted patches must also be copyrighted by Glyph.
I'd have to clarify that with my employer, but that shouldn't be a problem, as we regularly contribute back to Free Software packages we use if we have something worthwhile to contribute. Bernhard -- Intevation GmbH http://intevation.de/ Sketch http://sketch.sourceforge.net/ MapIt! http://www.mapit.de/
![](https://secure.gravatar.com/avatar/48ef90b5c7ec4c8a32fb9fb78856f53d.jpg?s=120&d=mm&r=g)
I wrote: [Mixin classes for tests that use reactors]
The test base class is finally publically available inder GPL for now: http://www.intevation.de/cgi-bin/viewcvs-greater.cgi/*checkout*/GREAT-ERModel/test/reactormixins.py?rev=1.1&content-type=text/plain
Someone asked for a usage example in private mail. There are a few test cases in the same CVS repository that use the mix-ins, but more illustrative is probably a very simple example like this: import unittest from reactormixins import ReactorMixin class Test(unittest.TestCase, ReactorMixin): def setUp(self): """Extend inherited method to set later_has_been_called to 0""" self.later_has_been_called = 0 ReactorMixin.setUp(self) def test(self): """Simple test to show how to use ReactorMixin""" # scheduler self.later to be run after 0.1 seconds self.callLater(0.1, self.later) # run the reactor with a 10s time out self.run_reactor(10) self.assert_(self.later_has_been_called) def later(self): """Record that later has been called and stop the reactor""" self.later_has_been_called = 1 self.stop_reactor() if __name__ == "__main__": unittest.main() This example doesn't do any networking but should give an idea of how to use the mixin classes. The networking support in the mixins is currently limited to listening because that all I needed so far. My testcases start sub-processes that connect to the reactor in the test case. Bernhard -- Intevation GmbH http://intevation.de/ Sketch http://sketch.sourceforge.net/ MapIt! http://www.mapit.de/
![](https://secure.gravatar.com/avatar/3a7e70f3ef2ad1539da42afc85c8d09d.jpg?s=120&d=mm&r=g)
On Wed, Dec 04, 2002 at 07:14:17PM +0100, Bernhard Herzog wrote:
We'd *really* appreciate such a thing. A lot of the existing tests are so ugly that it's hard to extend them right now, and something like this would make it a lot easier to clean all of these up. Also, as moshez said, Glyph needs the copyright on contributions, but you can still retain your own copyright on it. -- Chris Armstrong << radix@twistedmatrix.com >> http://twistedmatrix.com/users/radix.twistd/
participants (3)
-
Bernhard Herzog
-
Christopher Armstrong
-
Moshe Zadka