Twisted中的单元测试 (Unit Tests in Twisted)

  • 每个单元测试用来测试一个功能点。单元测试完全自动进行,执行迅速。对于整个系统的单元测试被整合到一个测试集合中,整个测试集可以批量执行。单元测试的结果是十分简单:通过或是不通过。所有这些意味着你可以方便的在任何时候测试你系统,并且能够迅速看到测试结果是通过还是没通过。 Each unit test tests one bit of functionality in the software. Unit tests are entirely automated and complete quickly. Unit tests for the entire system are gathered into one test suite, and may all be run in a single batch. The result of a unit test is simple: either it passes, or it doesn't. All this means you can test the entire system at any time without inconvenience, and quickly see what passes and what fails.

1. Twisted中的单元测试哲学(Unit Tests in the Twisted Philosophy)

  • Twisted开发小组坚持实践极限编程的思想,而使用单元测试的正是极限编程的基础。单元测试是一个不断增长你信心的工具。假设你修改了一个算法,你想知道你是否做错了什么东西吗?请运行单元测试。如果某个测试失败,你能够很清楚的知道哪儿出了问题,因为每个测试只覆盖了很少量的代码,而你清楚的知道你对代码做过怎样的修改。如果所有的测试都通过了,那么没问题,你可以继续了,你再也不需要担心你的修改可能会无意中影响了其它人的程序了。 The Twisted development team adheres to the practice of Extreme Programming (XP), and the usage of unit tests is a cornerstone XP practice. Unit tests are a tool to give you increased confidence. You changed an algorithm -- did you break something? Run the unit tests. If a test fails, you know where to look, because each test covers only a small amount of code, and you know it has something to do with the changes you just made. If all the tests pass, you're good to go, and you don't need to second-guess yourself or worry that you just accidently broke someone else's program.

2. 什么是应该测试的,什么是不应该测试的(What to Test, What Not to Test)

  • 你不必为每个方法都编写测试程序,只需为那些可能会发生变化的方法编写测试程序就够了。
    • --Kent Beck,极限编程解析,58页。
    You don't have to write a test for every single method you write, only production methods that could possibly break.
    • -- Kent Beck, Extreme Programming Explained, p. 58.

3. 运行测试程序(Running the Tests)

  • == 怎样运行(How) ==
    •     $ Twisted/admin/runtests
          
      在你的Emacs初始化文件中加上下面的代码,这会极大的方便你在Emacs中执行单元测试。 You'll find that having something like this in your emacs init files is quite handy:
          (defun runtests () (interactive)
            (compile "python /somepath/Twisted/admin/runtests"))
      
          (global-set-key [(alt t)] 'runtests)
          
    == 什么时候(When) ==
    • 永远要在提交代码前确定你的代码已经通过了所有的测试。如果其他人开始开发,却发现刚刚checkout到的代码无法通过测试,他们可能会非常生气并把你揪出来。 Always always always be sure all the tests pass before committing any code. If someone else checks out code at the start of a development session and finds failing tests, they will not be happy and may decide to hunt you down. 由于开发队伍的成员分布在世界各地,那个能够帮助你调好代码的人可能并不你的隔壁。你可能想把你的工作进展与整个网络上的同伴分享,但是你又必须保证CVS的主版本树永远保持一个正确的状态。此时,你需要使用分支技术,当你解决了你的问题,并通过了所有的测试之后,你就可以把你的修改合并到主版本中了。 Since this is a geographically dispersed team, the person who can help you get your code working probably isn't in the room with you. You may want to share your work in progress over the network, but you want to leave the main CVS tree in good working order. So use a branch, and merge your changes back in only after your problem is solved and all the unit tests pass again.

4. 加入一个测试程序(Adding a Test)

  • 在没有向Twisted中加入相应的测试程序之前,请不要向Twisted中加入新的模块。否则,你可能会在毫不知情的情况下破坏了你的模块。当你知道出了问题以后(可能是马上就知道,也可能是在一个release版本之后),由于已经做过太多的修改,你根本无法确定到底是那里出了问题。 Please don't add new modules to Twisted without adding tests for them too. Otherwise we could change something which breaks your module and not find out until later, making it hard to know exactly what the change that broke it was, or until after a release, and nobody wants broken code in a release.

    所有的测试程序都放在Twisted/twisted/test/目录下,测试程序的名称由“test_”前缀加上对应模块或是包的名称组成。在下面的“链接”这个小节中可以找到很多用PyUnit框架编写单元测试的文档。

    Tests go in Twisted/twisted/test/, and are named test_foo.py, where foo is the name of the module or package being tested. Extensive documentation on using the PyUnit framework for writing unit tests can be found in the links section below.

    一个与标准PyUnit文档像背离的事实:为了确保测试结果的任何变化都是由代码或是测试环境所造成的,而与测试的过程无关,Twisted自己移植了一个兼容的测试框架。这就意味着,无论何时想要导入单元测试模块,你都应该使用 from twisted.trial import unittest 来代替标准的 import unittest 

    One deviation from the standard PyUnit documentation: To ensure that any variations in test results are due to variations in the code or environment and not the test process itself, Twisted ships with its own, compatible, testing framework. That just means that when you import the unittest module, you will from twisted.trial import unittest instead of the standard import unittest. 只要遵循模块文件的命名与放置习惯,runtests就能聪明的找到你所加入的新的测试程序。 As long as you have followed the module naming and placement conventions, runtests will be smart enough to pick up any new tests you write.

5. 忽略测试,TODO项目(Skipping tests, TODO items)

  • Twisted单元测试框架--Trial,对PyUnit做了一些小的扩展,用来鼓励开发者添加新的测试程序。测试程序在测试一些可选功能时常见的情况是:可能需要依赖于某些特定的外部库文件是否可用,可能只能工作在特定的操作系统上。最重要的问题是,没有人把这些限制当作一个bug。 Trial, the Twisted unit test framework, has some extensions which are designed to encourage developers to add new tests. One common situation is that a test exercises some optional functionality: maybe it depends upon certain external libraries being available, maybe it only works on certain operating systems. The important common factor is that nobody considers these limitations to be a bug.

    为了使测试尽可能的简便,在特定的环境下某些测试可能会被忽略。单个的测试用例可以抛出SkipTest异常来指出这些测试应该被忽略,于是这个测试程序中其余部分就不再被执行。在测试结果summary部分,除了会统计通过或是不通过的测试用例的个数以外,还会对测试中被忽略部分做专门的统计。抛出SkipTest异常这种方式也可以用在条件语句中,用来判断是否满足程序执行的必要条件。

    To make it easy to test as much as possible, some tests may be skipped in certain situations. Individual test cases can raise the SkipTest exception to indicate that they should be skipped, and the remainder of the test is not run. In the summary (the very last thing printed, at the bottom of the test output) the test is counted as a skip instead of a success or fail. This should be used inside a conditional which looks for the necessary prerequisites:

      def testSSHClient(self):
          if not ssh_path:
              raise unittest.SkipTest, "cannot find ssh, nothing to test"
          foo() # do actual test after the SkipTest
      
    你也可以在一个方法上设定skip属性,这个属性描述了测试为什么被忽略。使用这种机制可以十分方便的临时取消一个测试用例,当然也根据情况手动修改一个类的属性。 You can also set the .skip attribute on the method, with a string to indicate why the test is being skipped. This is convenient for temporarily turning off a test case, but it can also be set conditionally (by manipulating the class attributes after they've been defined):
      def testThing(self):
          dotest()
      testThing.skip = "disabled locally"
    
      class MyTestCase(unittest.TestCase):
          def testOne(self):
              ...
          def testThing(self):
              dotest()
    
      if not haveThing:
          MyTestCase.testThing.im_func.skip = "cannot test without Thing"
          # but testOne() will still run
      
    最后,你也可以通过设置对应类的skip属性来一次性取消整个测试用例。如果你根据代码的功能依赖关系定义测试程序,那么这种方式可以很方便的取消某种现在还不支持的功能的所需要的单元测试。

    Finally, you can turn off an entire TestCase at once by setting the .skip attribute on the class. If you organize your tests by the functionality they depend upon, this is a convenient way to disable just the tests which cannot be run.

      class SSLTestCase(unittest.TestCase):
         ...
      class TCPTestCase(unittest.TestCase):
         ...
    
      if not haveSSL:
          SSLTestCase.skip = "cannot test without SSL support"
          # but TCPTestCase will still run
      
    == todo属性与测试新功能(.todo and Testing New Functionality) ==
    • 产生于极限编程开发过程中的两个好的实践方法有时候看上去好像相互矛盾: Two good practices which arise from the XP development process are sometimes at odds with each other:
      • 单元测试是一个好东西。好的开发者一发现单元测试失败就应该心怀恐惧。他们应该把所有的更改都去掉,直到代码能够顺利通过测试。
      • Unit tests are a good thing. Good developers recoil in horror when they see a failing unit test. They should drop everything until the test has been fixed.
      • 好的开发者应该先写单元测试。测试代码完成以后,他们才开始编写具体的实现代码,并使之能够通过单元测试。然后当前的开发就可以告一段落了。
      • Good developers write the unit tests first. Once tests are done, they write implementation code until the unit tests pass. Then they stop.
      这两个目标有的时候是彼此矛盾的。在编写任何实现代码之前,就编写单元测试,这注定一定会失败的。我们总想让开发者快点儿提交代码,以提高代码的可靠性并改善研究同一问题的多个开发人员之间的协作程度。当一个开发人员为代码添加新特性的时候,其它(其工作不涉及到新特性)的开发者本应当不必关注新代码所引入的错误。而不是在一个模块还没有完成,且未开始其单元测试的时候,就高喊“狼来了”,同时不让我们头脑中业已形成的由“狼来了”所导致的测试失败恐惧综合症有丝毫的淡化。如果真这么干,其结果要么是教会模块作者放弃该模块,或者是逼迫其在实现所有的功能后再提交他们的单元测试代码,要么是教会其它的开发者忽略测试程序所检查到的失败信息。这两种方式都不好。 These two goals will sometimes conflict. The unit tests that are written first, before any implementation has been done, are certain to fail. We want developers to commit their code frequently, for reliability and to improve coordination between multiple people working on the same problem together. While the code is being written, other developers (those not involved in the new feature) should not have to pay attention to failures in the new code. We should not dilute our well-indoctrinated Failing Test Horror Syndrome by crying wolf when an incomplete module has not yet started passing its unit tests. To do so would either teach the module author to put off writing or committing their unit tests until after all the functionality is working, or it would teach the other developers to ignore failing test cases. Both are bad things. 为了解决这个问题,我们使用了todo属性。当一个开发者第一次开始为某个尚未实现的新功能编写单元测试的时候,他们可以将该测试方法的todo属性设置为“期望失败”。该方法的运行仍然会产生失败,但是其产生的失败并不会被作为普通的失败加以记录,而是将这种“被期望的失败”放到一个单独的“期望失败”的统计类别中。开发者应该学会将“期望失败”与“实际失败”这两者区别对待,“期望失败”的优先级要比“实际失败”的优先级低一些,应该先解决那些导致“实际失败”的问题。
    • todo is intended to solve this problem. When a developer first starts writing the unit tests for functionality that has not yet been implemented, they can set the .todo attribute on the test methods that are expected to fail. These methods will still be run, but their failure will not be counted the same as normal failures: they will go into an expected failures category. Developers should learn to treat this category as a second-priority queue, behind actual test failures. 当开发者实现了某个特性之后,对应的测试程序终于能够正常运行了。但是由于已经存在的todo属性将这些测试都被标记为“期望失败”,能够成功通过的测试程序反而会把成功信息放到一个“不被期望的成功”的类别中。此时,开发者就应该将测试程序中的todo属性去掉了。从这一点开始,我们才开始正常的测试,而任何一点儿失败都将会引发整个开发团队的立即反映。 As the developer implements the feature, the tests will eventually start passing. This is surprising: after all those tests are marked as being expected to fail. The .todo tests which nevertheless pass are put into a unexpected success category. The developer should remove the .todo tag from these tests. At that point, they become normal tests, and their failure is once again cause for immediate action by the entire development team. 整个测试的生命周期可以描述为:
      1. 建立测试,将其todo属性标记为“期望失败”,此时的测试失败是一种“被期望失败”。
      2. 代码实现某个特定的功能,测试通过。此时的通过是一种“不被期望的通过”。
      3. 去掉todo标签。测试通过,此时的通过是正常通过。
      4. 修改代码,导致测试失败。开发者发现这种失败,并有所行动。
      5. 修复代码,使得测试再次成功,代码暂时稳定。
      The life cycle of a test is thus:
      1. Test is created, marked .todo. Test fails: expected failure.
      2. Code is written, test starts to pass. unexpected success.
      3. .todo tag is removed. Test passes. success.
      4. Code is broken, test starts to fail. failure. Developers spring into action.
      5. Code is fixed, test passes once more. success.
      任何长时间保持todo标签没有去掉的测试都应该被仔细检查。因为这可能反映出某个功能长期没有人维护,或是当前的代码已经无法通过测试,需要尽快修复。 Any test which remains marked with .todo for too long should be examined. Either it represents functionality which nobody is working on, or the test is broken in some fashion and needs to be fixed.

6. 如何关联测试用例与被测试的代码(Associating Test Cases With Source Files)

  • 请在需要测试的代码中添加一个test-case-name的标签,并确认你的新测试程序的测试范围已经覆盖了这个待测试文件的功能点。添加了test-case-name标签的待测试文件的头部应该像这样: Please add a test-case-name tag to the source file that is covered by your new test. This is a comment at the beginning of the file which looks like one of the following:
      # -*- test-case-name: twisted.test.test_defer -*-
      
    或是这样: or
      #!/usr/bin/python
      # -*- test-case-name: twisted.test.test_defer -*-
      

    Emacs能够理解这个格式,并标记一个文件变量。这么做的目的是为了让Emacs能够在文件的头一两行就能够获取test-case-name的值,而不是放在文件的尾部的Emacs文件变量列表中。如果你还需要定义其它的Emacs文件变量,你既可以使用前面介绍的方法,在文件的头两行中用分号分隔多个变量的定义,和可以采用Emacs支持的放在文件结尾的文件变量列表方式。(译者注:可以参考Emacs手册中 AD.2.5 Local Variables in Files 这个小节的说明,google上search一下吧,_This format is understood by emacs to mark File Variables. The intention is to accept test-case-name anywhere emacs would on the first or second line of the file (but not in the File Variables: block that emacs accepts at the end of the file). If you need to define other emacs file variables, you can either put them in the File Variables: block or use a semicolon-separated list of variable definitions:

      # -*- test-case-name: twisted.test.test_defer; fill-column: 75; -*-
      
    如果一份源码需要使用多个测试用例来测试,多个测试用例的名称使用逗号隔开。如下所示(注意,可以通过trial --testmodule检测所有的测试用例,但不是所有的工具都支持这种用法。): If the code is exercised by multiple test cases, those may be marked by using a comma-separated list of tests, as follows: (NOTE: not all tools can handle this yet.. trial --testmodule does, though)
      # -*- test-case-name: twisted.test.test_defer,twisted.test.test_tcp -*-
      
    trial --testmodule twisted/dir/myfile.py会使用test-case-name标签来检测为了完整测试mylife.py这个文件都需要运行那些测试用例。许多工具(包括twisted-dev.el的F9命令)都使用这种方式来正确的运行所有的测试用例。 The test-case-name tag will allow trial --testmodule twisted/dir/myfile.py to determine which test cases need to be run to exercise the code in myfile.py. Several tools (as well as twisted-dev.el's F9 command) use this to automatically run the right tests.

7. 一些Twisted中的特殊东西:reactor、Defferred和callLater(Twisted-specific quirks: reactor, Deferreds, callLater)

  • 对于具有线性控制流的代码来说,标准Python测试框架是一个理想的测试工具,Twisted中的Trial也是从它衍生而来。但是Twisted是一个异步网络框架,它提供一个简洁明确的方式以建立“响应某种事件(例如timer和数据到来)”的函数,这些函数通常具有高度非线性化的控制流。Trial就是在标准Python测试框架的基础上做了一些扩展,用来测试这种类型的代码。本小节给出了一些如何使用这些扩展以及如何最佳化你的测试结构的建议建议。 The standard Python unittest framework, from which Trial is derived, is ideal for testing code with a fairly linear flow of control. Twisted is an asynchronous networking framework which provides a clean, sensible way to establish functions that are run in response to events (like timers and incoming data), which creates a highly non-linear flow of control. Trial has a few extensions which help to test this kind of code. This section provides some hints on how to use these extensions and how to best structure your tests. == (恢复reactor的状态)Leave the Reactor as you found it ==
    • Trial只使用一个线程,只使用一个reactor来运行整个测试集。因此当一个测试程序结束以后,应该将reactor的状态复原。遗留的timer可能会使随后进行测试的其它人的程序意外超时。遗留的连接请求可能会在随后的测试中成功返回或是失败。这些都会导致测试程序间歇性产生失败,于是你不得不一个测试一个测试的检查,并花费大量的时间去追综代码。 Trial runs the entire test suite (over one thousand tests) in a single process, with a single reactor. Therefore it is important that your test leave the reactor in the same state as it found it. Leftover timers may expire during somebody else's unsuspecting test. Leftover connection attempts may complete (and fail) during a later test. These lead to intermittent failures that wander from test to test and are very time-consuming to track down. 你的测试应该自己负责打扫战场。tearDown方法就是放置这些清理代码的理想位置:无论你的测试是否通过,这个方法总会被执行(这个情况类似于try-except中的一个不指定任何异常规范的空except语句)。tearDown方法中产生的异常会被视为错误,并直接导致测试失败退出。 Your test is responsible for cleaning up after itself. The tearDown method is an ideal place for this cleanup code: it is always run regardless of whether your test passes or fails (like a bare except clause in a try-except construct). Exceptions in tearDown are flagged as errors and flunk the test.

      未来要增加的辅助函数:TestCase.addPort、TestCase.addTimer

      TODO: helper functions: TestCase.addPort, TestCase.addTimer 最好不要使用reactor.stop方法,除非是在那些专门测试reactor的测试用例中(这些用例清楚的知道如何修复已经破坏的状态)。如果你一定要使用reactor.run方法,那么请使用reactor.crash代替reactor.stop来停掉reactor。 reactor.stop is considered very harmful, and should only be used by reactor-specific test cases which know how to restore the state that it kills. If you must use reactor.run, use reactor.crash to stop it instead of reactor.stop. Trial会尽力确保每个测试结束后reactor都是干净的,但是reactor本身并没有一个可以很好的支持这种工作的接口。reactor可以发现遗留的timer,但是无法找到遗留的socket连接。 Trial tries to help insure that the reactor is clean after each test, but the reactor does not yet support an interface that would make this work properly. It can catch leftover timers, but not lingering sockets.

    == deferredResult方法(deferredResult) ==
    • 如果你的测试程序用了Defferred,但是你只是想得到Defferred的最终结果,而不是使用其异步执行的能力,那么你可以使用defferredResult方法。这个方法会等待Defferred的结果被发出,然后他才从函数调用中返回。如果Defferred进入了errback中,这个方法就会产生一个异常,并导致你的测试失败退出。注意:只有当Defferred的结果被发出的时候,defferredResult方法结束调用(timer是个例外,timer所产生的异常不会使这个方法结束返回)。 If your test creates a Deferred and simply wants to verify something about its result, use deferredResult. It will wait for the Deferred to fire and give you the result. If the Deferred runs the errback handler instead, it will raise an exception so your test can fail. Note that the only thing that will terminate a deferredResult call is if the Deferred fires; in particular, timers which raise exceptions will not cause it to return.
    == 事件等待(Waiting for Things) ==
    • 执行那些“需要等待某些事件发生”的测试程序的一个较好的方法是使用一个while not self.done循环,然后在每次循环开始的时候执行reactor.iterate(0.1)。参数0.1表示reactor空闲等待的最长时间是100毫秒,超时就返回。100毫秒已经足可以避免“reactor抢占了所有的CPU执行时间,而你的timer却在傻傻的等待超时到来”情况的发生。 The preferred way to run a test that waits for something to happen (always triggered by other things that you have done) is to use a while not self.done loop that does reactor.iterate(0.1) at the beginning of each pass. The 0.1 argument sets a limit on how long the reactor will wait to return if there is nothing to do. 100 milliseconds is long enough to avoid spamming the CPU while your timers wait to expire.
    == 使用timer来检测失败的测试(Using Timers to Detect Failing Tests) ==
    • 通常,我们应该为测试程序建立某种自动防止无限期等待的超时机制,该机制会在“发生了某些意想不到的异常且测试程序中正常的失败处理分支被跳过”的情况下终止程序的执行。这个超时机制可以设定某个测试程序能够执行的时间上限,以保证整个测试集不会由于某个单个测试程序的失败而产生延迟。对于Twisted测试集来说,这尤其重要。因为每当有修改被提交到CVS中时,buildbot就会自动执行Twisted的测试集(译者注:BuildBot是一个自动编译/测试工具,具体参见http://buildbot.sourceforge.net/)。 It is common for tests to establish some kind of fail-safe timeout that will terminate the test in case something unexpected has happened and none of the normal test-failure paths are followed. This timeout puts an upper bound on the time that a test can consume, and prevents the entire test suite from stalling because of a single test. This is especially important for the Twisted test suite, because it is run automatically by the buildbot whenever changes are committed to the CVS repository.

      Trial测试通过产生一个FailTest异常来表示测试失败(self.fail及其它一些类似的方法都覆盖了这个产生异常的raise语句,可以直接使用这些方法来代替raise语句)。在一个callRemote timer中产生的异常会被捕获,但它只是将异常记录在日志里,而不会做其它的事儿。Trial可以用“日志hook”找到在刚刚结束的测试中在什么时候发生了错误--就是这些错误将会导致测试失败,但是这一切都是在一个测试结束之后才发生的。而在你的测试程序的中无法获得“日志中记录的这些错误”--也就是说在测试结束之前,Trial无法获得任何新记录在日志中的错误信息。因此不能直接用callRemote timer来建立上面提到的超时机制。

      Trial tests indicate they have failed by raising a FailTest exception (self.fail and friends are just wrappers around this raise statement). Exceptions that are raised inside a callRemote timer are caught and logged but otherwise ignored. Trial uses a logging hook to notice when errors have been logged by the test that just completed (so such errors will flunk the test), but this happens after the fact: they will not be noticed by the main body of your test code. Therefore callRemote timers can not be used directly to establish timeouts which terminate and flunk the test. 实现这种超时机制的正确的方法是使用一个self.done标志,使用一个while循环来重复调用reactor,直到self.done标志为true。如果想要终止测试(测试成功或是失败),只要将self.done标志设为true就行了。 The right way to implement this sort of timeout is to have a self.done flag, and a while loop which iterates the reactor until it becomes true. Anything that causes the test to be finished (success or failure) can set self.done to cause the loop to exit. 通过reactor来运行的大多数Twisted代码都与socket活动有关。这种代码几乎总是开始于Protocol.connectionMade或是Protocol.dataRecevide方法(因为输出端会遍历整个数据传输队列)。以这种方式(在reactor中通过doRead或是doWrite)被调用的代码中所产生的异常会被捕获、记录在日志中并发送给connectionLost方法,除此以外不作其它的处理。 Most of the code in Twisted is run by the reactor as a result of socket activity. This is almost always started by Protocol.connectionMade or Protocol.dataReceived (because the output side goes through a buffer which queues data for transmission). Exceptions that are raised by code called in this way (by the reactor, through doRead or doWrite) are caught, logged, handed to connectionLost, and otherwise ignored. 这就意味着你的Protocol的connectionLost方法必须总是将self.done标志设为true,因为这个方法有可能会因为产生上述的异常而被调用。如果不这么做,测试程序就永远不会停止。 This means that your Protocol's connectionLost method, if invoked because of an exception, must also set this self.done flag. Otherwise the test will not terminate. 在Defferred的callback中产生的异常会被转换为一个Failure,保存在Defferred中。当为Defferred添加了一个errBack时,这个Failure就会传递给这个errback。如果Defferred已经返回,但是仍然存在未处理的错误,这些错误就会和timer或是protocol里的几个方法里产生的异常一样被记录到日志中。这最终会导致当前的测试失败,但是正像上面描述的那样,在此时失败结束之前,你无法检测到这些错误。因此再次强调一下,最好为你的Defferred加一个errback,它会在测试出错的情况下帮助你退出主循环并结束测试。 Exceptions that are raised in a Deferred callback are turned into a Failure and stashed inside the Deferred. When an errback handler is attached, the Failure is given to it. If the Deferred goes out of scope while an error is still pending, the error is logged just like exceptions that happen in timers or protocol handlers. This will cause the current test to flunk (eventually), but it is not checked until after the test fails. So again, it is a good idea to add errbacks to your Deferreds that will terminate your test's main loop. 下面这个简单的例子演示了这些技术的应用方法: Here is a brief example that demonstrates a few of these techniques.

          class MyTest(unittest.TestCase):
              def setUp(self):
                  self.done = False
                  self.failure = None
      
              def tearDown(self):
                  self.server.stopListening()
                  # TODO: also shut down client
                  try:
                      self.timeout.cancel()
                  except (error.AlreadyCancelled, error.AlreadyCalled):
                      pass
      
              def succeeded(self):
                  self.done = True
      
              def failed(self, why):
                  self.done = True
                  self.failure = why
      
              def testServer(self):
                  self.server = reactor.listenTCP(port, factory)
                  self.client = reactor.connectTCP(port, factory)
                  # you should give the factories a way to call our 'succeeded' or
                  # 'failed' methods
                  self.timeout = reactor.callLater(5, self.failed, "timeout")
                  while not self.done:
                      reactor.iterate(0.1)
      
                  # we get here if the test is finished, for good or for bad
                  if self.failure:
                      self.fail(self.failure)
                  # otherwise it probably passed. Cleanup will be done in tearDown()
          

8. 链接(Links)

<< 返回(PyTwisted/WorkingOnTheTwistedCodeBase

版本:1.3.0 Version: 1.3.0

by bigbaboon, 2004-09-06

9. Discuss

  • great! but all about Emacs??? full demo code? or about Vim?? thanx for all -- ZoomQuiet