1 <?xml version="1.0" encoding="utf-8"?><!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'><html lang="en" xmlns="http://www.w3.org/1999/xhtml">
3 <title>Twisted Documentation: Test-driven development with Twisted</title>
4 <link href="stylesheet.css" rel="stylesheet" type="text/css"/>
8 <h1 class="title">Test-driven development with Twisted</h1>
9 <div class="toc"><ol><li><a href="#auto0">Introductory example of Python unit testing</a></li><li><a href="#auto1">Creating an API and writing tests</a></li><li><a href="#auto2">Making the tests pass</a></li><ul><li><a href="#auto3">Factoring out common test logic</a></li></ul><li><a href="#auto4">Twisted specific testing</a></li><li><a href="#auto5">Testing a protocol</a></li><ul><li><a href="#auto6">Creating and testing the server</a></li><li><a href="#auto7">Creating and testing the client</a></li></ul><li><a href="#auto8">More good practices</a></li><ul><li><a href="#auto9">Testing scheduling</a></li><li><a href="#auto10">Cleaning up after tests</a></li><li><a href="#auto11">Handling logged errors</a></li></ul><li><a href="#auto12">Resolve a bug</a></li><li><a href="#auto13">Code coverage</a></li><li><a href="#auto14">Conclusion</a></li></ol></div>
14 <p>Writing good code is hard, or at least it can be. A major challenge is
15 to ensure that your code remains correct as you add new functionality.</p>
17 <p><a href="http://en.wikipedia.org/wiki/Unit_test" shape="rect">Unit testing</a> is a
18 modern, light-weight testing methodology in widespread use in many
19 programming languages. Development that relies on unit tests is often
20 referred to as Test-Driven Development
21 (<a href="http://en.wikipedia.org/wiki/Test-driven_development" shape="rect">TDD</a>).
22 Most Twisted code is tested using TDD.</p>
24 <p>To gain a solid understanding of unit testing in Python, you should read
25 the <a href="http://docs.python.org/library/unittest.html" shape="rect">unittest --
26 Unit testing framework chapter</a> of the <a href="http://docs.python.org/library/index.html" shape="rect">Python Library
27 Reference</a>. There is also a ton of information available online and in
30 <h2>Introductory example of Python unit testing<a name="auto0"/></h2>
32 <p>This document is principally a guide to Trial, Twisted's unit testing
33 framework. Trial is based on Python's unit testing framework. While we do not
34 aim to give a comprehensive guide to general Python unit testing, it will be
35 helpful to consider a simple non-networked example before expanding to cover a
36 networking code that requires the special capabilities of Trial. If you are
37 already familiar with unit test in Python, jump straight to the section
38 specific to <a href="#twisted" shape="rect">testing Twisted code</a>.</p>
40 <p><div class="note"><strong>Note: </strong>In what follows we will make a series of refinements
41 to some simple classes. In order to keep the examples and source code links
42 complete and to allow you to run Trial on the intermediate results at every
43 stage, I add <code>_N</code> (where the <code>N</code> are successive
44 integers) to file names to keep them separate. This is a minor visual
45 distraction that should be ignored.</div></p>
47 <h2>Creating an API and writing tests<a name="auto1"/></h2>
49 <p>We'll create a library for arithmetic calculation. First, create a
50 project structure with a directory called <code class="shell">calculus</code> containing an empty <code class="py-filename">__init__.py</code> file.</p>
52 <p>Then put the following simple class definition API into <code class="py-filename">calculus/base_1.py</code>:</p>
54 <div class="py-listing"><pre><p class="py-linenumber"> 1
70 </p><span class="py-src-comment"># -*- test-case-name: calculus.test.test_base_1 -*-</span>
74 <span class="py-src-keyword">class</span> <span class="py-src-identifier">Calculation</span>(<span class="py-src-parameter">object</span>):
75 <span class="py-src-keyword">def</span> <span class="py-src-identifier">add</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
76 <span class="py-src-keyword">pass</span>
78 <span class="py-src-keyword">def</span> <span class="py-src-identifier">subtract</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
79 <span class="py-src-keyword">pass</span>
81 <span class="py-src-keyword">def</span> <span class="py-src-identifier">multiply</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
82 <span class="py-src-keyword">pass</span>
84 <span class="py-src-keyword">def</span> <span class="py-src-identifier">divide</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
85 <span class="py-src-keyword">pass</span>
86 </pre><div class="caption">Source listing - <a href="listings/trial/calculus/base_1.py"><span class="filename">listings/trial/calculus/base_1.py</span></a></div></div>
88 <p>(Ignore the <code class="python">test-case-name</code> comment for
89 now. You'll see why that's useful <a href="#comment" shape="rect">below</a>.)</p>
91 <p>We've written the interface, but not the code. Now we'll write a set of
92 tests. At this point of development, we'll be expecting all tests to
93 fail. Don't worry, that's part of the point. Once we have a test framework
94 functioning, and we have some decent tests written (and failing!), we'll go
95 and do the actual development of our calculation API. This is the preferred
96 way to work for many people using TDD - write tests first, make sure they
97 fail, then do development. Others are not so strict and write tests after
98 doing the development.</p>
100 <p>Create a <code class="shell">test</code> directory beneath <code class="shell">calculus</code>, with an empty <code class="py-filename">__init__.py</code> file. In a <code class="py-filename">calculus/test/test_base_1.py</code>, put the
103 <div class="py-listing"><pre><p class="py-linenumber"> 1
126 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">calculus</span>.<span class="py-src-variable">base_1</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Calculation</span>
127 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">trial</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">unittest</span>
129 <span class="py-src-keyword">class</span> <span class="py-src-identifier">CalculationTestCase</span>(<span class="py-src-parameter">unittest</span>.<span class="py-src-parameter">TestCase</span>):
130 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_add</span>(<span class="py-src-parameter">self</span>):
131 <span class="py-src-variable">calc</span> = <span class="py-src-variable">Calculation</span>()
132 <span class="py-src-variable">result</span> = <span class="py-src-variable">calc</span>.<span class="py-src-variable">add</span>(<span class="py-src-number">3</span>, <span class="py-src-number">8</span>)
133 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>(<span class="py-src-variable">result</span>, <span class="py-src-number">11</span>)
135 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_subtract</span>(<span class="py-src-parameter">self</span>):
136 <span class="py-src-variable">calc</span> = <span class="py-src-variable">Calculation</span>()
137 <span class="py-src-variable">result</span> = <span class="py-src-variable">calc</span>.<span class="py-src-variable">subtract</span>(<span class="py-src-number">7</span>, <span class="py-src-number">3</span>)
138 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>(<span class="py-src-variable">result</span>, <span class="py-src-number">4</span>)
140 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_multiply</span>(<span class="py-src-parameter">self</span>):
141 <span class="py-src-variable">calc</span> = <span class="py-src-variable">Calculation</span>()
142 <span class="py-src-variable">result</span> = <span class="py-src-variable">calc</span>.<span class="py-src-variable">multiply</span>(<span class="py-src-number">12</span>, <span class="py-src-number">5</span>)
143 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>(<span class="py-src-variable">result</span>, <span class="py-src-number">60</span>)
145 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_divide</span>(<span class="py-src-parameter">self</span>):
146 <span class="py-src-variable">calc</span> = <span class="py-src-variable">Calculation</span>()
147 <span class="py-src-variable">result</span> = <span class="py-src-variable">calc</span>.<span class="py-src-variable">divide</span>(<span class="py-src-number">12</span>, <span class="py-src-number">5</span>)
148 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>(<span class="py-src-variable">result</span>, <span class="py-src-number">2</span>)
149 </pre><div class="caption">Source listing - <a href="listings/trial/calculus/test/test_base_1.py"><span class="filename">listings/trial/calculus/test/test_base_1.py</span></a></div></div>
151 <p>You should now have the following 4 files:
153 <pre class="shell" xml:space="preserve">
156 calculus/test/__init__.py
157 calculus/test/test_base_1.py
161 <p>To run the tests, there are two things you must get set up. Make sure
162 you get these both done - nothing below will work unless you do.</p>
164 <p>First, make sure that the directory that <em>contains</em> your
165 <code class="shell">calculus</code> directory is in your Python load path. If you're
166 using the Bash shell on some form of unix (e.g., Linux, Mac OS X), run
167 <code class="shell">PYTHONPATH="$PYTHONPATH:`pwd`/.."</code> at
168 the command line in the <code class="shell">calculus</code> directory. Once you have your
169 Python path set up correctly, you should be able to run Python from the
170 command line and <code class="python">import calculus</code> without seeing
173 <p>Second, make sure you can run the <code class="shell">trial</code>
174 command. That is, make sure the directory containing the <code class="shell">trial</code>
175 program on you system is in your shell's <code class="shell">PATH</code>. The easiest way to check if you have this is to
176 try running <code class="shell">trial --help</code> at the command line. If
177 you see a list of invocation options, you're in business. If your shell
178 reports something like <code class="shell">trial: command not found</code>,
179 make sure you have Twisted installed properly, and that the Twisted
180 <code class="shell">bin</code> directory is in your <code class="shell">PATH</code>. If
181 you don't know how to do this, get some local help, or figure it out by
182 searching online for information on setting and changing environment
183 variables for you operating system.</p>
185 <p>With those (one-time) preliminary steps out of the way, let's perform
186 the tests. Run <code class="shell">trial calculus.test.test_base_1</code> from the
187 command line from the <code class="shell">calculus</code> directory.
189 You should see the following output (though your files are probably not in
190 <code class="shell">/tmp</code>:</p>
192 <pre class="shell" xml:space="preserve">
193 $ trial calculus.test.test_base_1
194 calculus.test.test_base_1
197 test_divide ... [FAIL]
198 test_multiply ... [FAIL]
199 test_subtract ... [FAIL]
201 ===============================================================================
203 Traceback (most recent call last):
204 File "/tmp/calculus/test/test_base_1.py", line 8, in test_add
205 self.assertEqual(result, 11)
206 twisted.trial.unittest.FailTest: not equal:
211 calculus.test.test_base_1.CalculationTestCase.test_add
212 ===============================================================================
214 Traceback (most recent call last):
215 File "/tmp/calculus/test/test_base_1.py", line 23, in test_divide
216 self.assertEqual(result, 2)
217 twisted.trial.unittest.FailTest: not equal:
222 calculus.test.test_base_1.CalculationTestCase.test_divide
223 ===============================================================================
225 Traceback (most recent call last):
226 File "/tmp/calculus/test/test_base_1.py", line 18, in test_multiply
227 self.assertEqual(result, 60)
228 twisted.trial.unittest.FailTest: not equal:
233 calculus.test.test_base_1.CalculationTestCase.test_multiply
234 ===============================================================================
236 Traceback (most recent call last):
237 File "/tmp/calculus/test/test_base_1.py", line 13, in test_subtract
238 self.assertEqual(result, 4)
239 twisted.trial.unittest.FailTest: not equal:
244 calculus.test.test_base_1.CalculationTestCase.test_subtract
245 -------------------------------------------------------------------------------
246 Ran 4 tests in 0.042s
251 <p>How to interpret this output? You get a list of the individual tests, each
252 followed by its result. By default, failures are printed at the end, but this
253 can be changed with the <code class="shell">-e</code> (or <code class="shell">--rterrors</code>) option.</p>
255 <p>One very useful thing in this output is the fully-qualified name of the
256 failed tests. This appears at the bottom of each =-delimited area of the
257 output. This allows you to copy and paste it to just run a single test you're
258 interested in. In our example, you could run <code class="shell">trial
259 calculus.test.test_base_1.CalculationTestCase.test_subtract</code> from the
262 <p>Note that trial can use different reporters to modify its output. Run
263 <code class="shell">trial --help-reporters</code> to see a list of
267 The tests can be run by <code class="shell">trial</code> in multiple ways:
269 <li><code class="shell">trial calculus</code>: run all the tests for the
270 calculus package.</li>
272 <li><code class="shell">trial calculus.test</code>: run using Python's
273 <code class="python">import</code> notation.</li>
275 <li><code class="shell">trial calculus.test.test_base_1</code>: as above, for
276 a specific test module. You can follow that logic by putting your class name
277 and even a method name to only run those specific tests.</li>
279 <li><a name="comment" shape="rect"/><code class="shell">trial
280 --testmodule=calculus/base_1.py</code>: use the <code class="python">test-case-name</code> comment in the first line of
281 <code class="py-filename">calculus/base_1.py</code> to find the tests.</li>
283 <li><code class="shell">trial calculus/test</code>: run all the tests in the
284 test directory (not recommended).</li>
286 <li><code class="shell">trial calculus/test/test_base_1.py</code>: run a
287 specific test file (not recommended).</li>
290 The first 3 versions using full qualified names are strongly encouraged: they
291 are much more reliable and they allow you to easily be more selective in your
295 <p>You'll notice that Trial create a <code class="shell">_trial_temp</code> directory in
296 the directory where you run the tests. This has a file called
297 <code class="shell">test.log</code> which contains the log output of the tests (created
298 using <code class="python">log.msg</code> or <code class="python">log.err</code> functions). Examine this file if you add
299 logging to your tests.</p>
301 <h2>Making the tests pass<a name="auto2"/></h2>
303 <p>Now that we have a working test framework in place, and our tests are
304 failing (as expected) we can go and try to implement the correct API. We'll do
305 that in a new version of the above base_1
306 module, <code class="py-filename">calculus/base_2.py</code>:</p>
308 <div class="py-listing"><pre><p class="py-linenumber"> 1
322 </p><span class="py-src-comment"># -*- test-case-name: calculus.test.test_base_2 -*-</span>
324 <span class="py-src-keyword">class</span> <span class="py-src-identifier">Calculation</span>(<span class="py-src-parameter">object</span>):
325 <span class="py-src-keyword">def</span> <span class="py-src-identifier">add</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
326 <span class="py-src-keyword">return</span> <span class="py-src-variable">a</span> + <span class="py-src-variable">b</span>
328 <span class="py-src-keyword">def</span> <span class="py-src-identifier">subtract</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
329 <span class="py-src-keyword">return</span> <span class="py-src-variable">a</span> - <span class="py-src-variable">b</span>
331 <span class="py-src-keyword">def</span> <span class="py-src-identifier">multiply</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
332 <span class="py-src-keyword">return</span> <span class="py-src-variable">a</span> * <span class="py-src-variable">b</span>
334 <span class="py-src-keyword">def</span> <span class="py-src-identifier">divide</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
335 <span class="py-src-keyword">return</span> <span class="py-src-variable">a</span> / <span class="py-src-variable">b</span>
336 </pre><div class="caption">Source listing - <a href="listings/trial/calculus/base_2.py"><span class="filename">listings/trial/calculus/base_2.py</span></a></div></div>
338 <p>We'll also create a new version of test_base_1 which imports and tests this
340 in <code class="py-filename">calculus/test_base_2.py</code>:</p>
342 <p><div class="py-listing"><pre><p class="py-linenumber"> 1
371 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">calculus</span>.<span class="py-src-variable">base_2</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Calculation</span>
372 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">trial</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">unittest</span>
376 <span class="py-src-keyword">class</span> <span class="py-src-identifier">CalculationTestCase</span>(<span class="py-src-parameter">unittest</span>.<span class="py-src-parameter">TestCase</span>):
378 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_add</span>(<span class="py-src-parameter">self</span>):
379 <span class="py-src-variable">calc</span> = <span class="py-src-variable">Calculation</span>()
380 <span class="py-src-variable">result</span> = <span class="py-src-variable">calc</span>.<span class="py-src-variable">add</span>(<span class="py-src-number">3</span>, <span class="py-src-number">8</span>)
381 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>(<span class="py-src-variable">result</span>, <span class="py-src-number">11</span>)
384 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_subtract</span>(<span class="py-src-parameter">self</span>):
385 <span class="py-src-variable">calc</span> = <span class="py-src-variable">Calculation</span>()
386 <span class="py-src-variable">result</span> = <span class="py-src-variable">calc</span>.<span class="py-src-variable">subtract</span>(<span class="py-src-number">7</span>, <span class="py-src-number">3</span>)
387 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>(<span class="py-src-variable">result</span>, <span class="py-src-number">4</span>)
390 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_multiply</span>(<span class="py-src-parameter">self</span>):
391 <span class="py-src-variable">calc</span> = <span class="py-src-variable">Calculation</span>()
392 <span class="py-src-variable">result</span> = <span class="py-src-variable">calc</span>.<span class="py-src-variable">multiply</span>(<span class="py-src-number">12</span>, <span class="py-src-number">5</span>)
393 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>(<span class="py-src-variable">result</span>, <span class="py-src-number">60</span>)
396 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_divide</span>(<span class="py-src-parameter">self</span>):
397 <span class="py-src-variable">calc</span> = <span class="py-src-variable">Calculation</span>()
398 <span class="py-src-variable">result</span> = <span class="py-src-variable">calc</span>.<span class="py-src-variable">divide</span>(<span class="py-src-number">12</span>, <span class="py-src-number">5</span>)
399 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>(<span class="py-src-variable">result</span>, <span class="py-src-number">2</span>)
400 </pre><div class="caption">test_base_2 - <a href="listings/trial/calculus/test/test_base_2.py"><span class="filename">listings/trial/calculus/test/test_base_2.py</span></a></div></div> is a copy of test_base_1, but with the import changed. Run <code class="shell">trial</code> again as above, and your tests should now pass:</p>
402 <pre class="shell" xml:space="preserve">
403 $ trial calculus.test.test_base_2
406 calculus.test.test_base
410 test_multiply ... [OK]
411 test_subtract ... [OK]
413 -------------------------------------------------------------------------------
414 Ran 4 tests in 0.067s
419 <h3>Factoring out common test logic<a name="auto3"/></h3>
421 <p>You'll notice that our test file contains redundant code. Let's get rid
422 of that. Python's unit testing framework allows your test class to define a
423 <code class="python">setUp</code> method that is called before
424 <em>each</em> test method in the class. This allows you to add attributes
425 to <code class="python">self</code> that can be used in tests
426 methods. We'll also add a parameterized test method to further simplify the
429 <p>Note that a test class may also provide the counterpart of <code class="python">setUp</code>, named <code class="python">tearDown</code>,
430 which will be called after <em>each</em> test (whether successful or
431 not). <code class="python">tearDown</code> is mainly used for post-test
432 cleanup purposes. We will not use <code class="python">tearDown</code>
435 <p>Create <code class="py-filename">calculus/test/test_base_2b.py</code> as
438 <div class="py-listing"><pre><p class="py-linenumber"> 1
467 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">calculus</span>.<span class="py-src-variable">base_2</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Calculation</span>
468 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">trial</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">unittest</span>
472 <span class="py-src-keyword">class</span> <span class="py-src-identifier">CalculationTestCase</span>(<span class="py-src-parameter">unittest</span>.<span class="py-src-parameter">TestCase</span>):
473 <span class="py-src-keyword">def</span> <span class="py-src-identifier">setUp</span>(<span class="py-src-parameter">self</span>):
474 <span class="py-src-variable">self</span>.<span class="py-src-variable">calc</span> = <span class="py-src-variable">Calculation</span>()
477 <span class="py-src-keyword">def</span> <span class="py-src-identifier">_test</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">operation</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>, <span class="py-src-parameter">expected</span>):
478 <span class="py-src-variable">result</span> = <span class="py-src-variable">operation</span>(<span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
479 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>(<span class="py-src-variable">result</span>, <span class="py-src-variable">expected</span>)
482 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_add</span>(<span class="py-src-parameter">self</span>):
483 <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">calc</span>.<span class="py-src-variable">add</span>, <span class="py-src-number">3</span>, <span class="py-src-number">8</span>, <span class="py-src-number">11</span>)
486 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_subtract</span>(<span class="py-src-parameter">self</span>):
487 <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">calc</span>.<span class="py-src-variable">subtract</span>, <span class="py-src-number">7</span>, <span class="py-src-number">3</span>, <span class="py-src-number">4</span>)
490 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_multiply</span>(<span class="py-src-parameter">self</span>):
491 <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">calc</span>.<span class="py-src-variable">multiply</span>, <span class="py-src-number">6</span>, <span class="py-src-number">9</span>, <span class="py-src-number">54</span>)
494 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_divide</span>(<span class="py-src-parameter">self</span>):
495 <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">calc</span>.<span class="py-src-variable">divide</span>, <span class="py-src-number">12</span>, <span class="py-src-number">5</span>, <span class="py-src-number">2</span>)
496 </pre><div class="caption">Source listing - <a href="listings/trial/calculus/test/test_base_2b.py"><span class="filename">listings/trial/calculus/test/test_base_2b.py</span></a></div></div>
498 <p>Much cleaner, no?</p>
500 <p>We'll now add some additional error tests. Testing just for successful
501 use of the API is generally not enough, especially if you expect your code
502 to be used by others. Let's make sure the <code class="python">Calculation</code> class raises exceptions if someone tries
503 to call its methods with arguments that cannot be converted to
506 <p>We arrive at <code class="py-filename">calculus/test/test_base_3.py</code>:</p>
508 <div class="py-listing"><pre><p class="py-linenumber"> 1
560 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">calculus</span>.<span class="py-src-variable">base_3</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Calculation</span>
561 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">trial</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">unittest</span>
565 <span class="py-src-keyword">class</span> <span class="py-src-identifier">CalculationTestCase</span>(<span class="py-src-parameter">unittest</span>.<span class="py-src-parameter">TestCase</span>):
566 <span class="py-src-keyword">def</span> <span class="py-src-identifier">setUp</span>(<span class="py-src-parameter">self</span>):
567 <span class="py-src-variable">self</span>.<span class="py-src-variable">calc</span> = <span class="py-src-variable">Calculation</span>()
570 <span class="py-src-keyword">def</span> <span class="py-src-identifier">_test</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">operation</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>, <span class="py-src-parameter">expected</span>):
571 <span class="py-src-variable">result</span> = <span class="py-src-variable">operation</span>(<span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
572 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>(<span class="py-src-variable">result</span>, <span class="py-src-variable">expected</span>)
575 <span class="py-src-keyword">def</span> <span class="py-src-identifier">_test_error</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">operation</span>):
576 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertRaises</span>(<span class="py-src-variable">TypeError</span>, <span class="py-src-variable">operation</span>, <span class="py-src-string">"foo"</span>, <span class="py-src-number">2</span>)
577 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertRaises</span>(<span class="py-src-variable">TypeError</span>, <span class="py-src-variable">operation</span>, <span class="py-src-string">"bar"</span>, <span class="py-src-string">"egg"</span>)
578 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertRaises</span>(<span class="py-src-variable">TypeError</span>, <span class="py-src-variable">operation</span>, [<span class="py-src-number">3</span>], [<span class="py-src-number">8</span>, <span class="py-src-number">2</span>])
579 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertRaises</span>(<span class="py-src-variable">TypeError</span>, <span class="py-src-variable">operation</span>, {<span class="py-src-string">"e"</span>: <span class="py-src-number">3</span>}, {<span class="py-src-string">"r"</span>: <span class="py-src-string">"t"</span>})
582 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_add</span>(<span class="py-src-parameter">self</span>):
583 <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">calc</span>.<span class="py-src-variable">add</span>, <span class="py-src-number">3</span>, <span class="py-src-number">8</span>, <span class="py-src-number">11</span>)
586 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_subtract</span>(<span class="py-src-parameter">self</span>):
587 <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">calc</span>.<span class="py-src-variable">subtract</span>, <span class="py-src-number">7</span>, <span class="py-src-number">3</span>, <span class="py-src-number">4</span>)
590 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_multiply</span>(<span class="py-src-parameter">self</span>):
591 <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">calc</span>.<span class="py-src-variable">multiply</span>, <span class="py-src-number">6</span>, <span class="py-src-number">9</span>, <span class="py-src-number">54</span>)
594 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_divide</span>(<span class="py-src-parameter">self</span>):
595 <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">calc</span>.<span class="py-src-variable">divide</span>, <span class="py-src-number">12</span>, <span class="py-src-number">5</span>, <span class="py-src-number">2</span>)
598 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_errorAdd</span>(<span class="py-src-parameter">self</span>):
599 <span class="py-src-variable">self</span>.<span class="py-src-variable">_test_error</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">calc</span>.<span class="py-src-variable">add</span>)
602 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_errorSubtract</span>(<span class="py-src-parameter">self</span>):
603 <span class="py-src-variable">self</span>.<span class="py-src-variable">_test_error</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">calc</span>.<span class="py-src-variable">subtract</span>)
606 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_errorMultiply</span>(<span class="py-src-parameter">self</span>):
607 <span class="py-src-variable">self</span>.<span class="py-src-variable">_test_error</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">calc</span>.<span class="py-src-variable">multiply</span>)
610 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_errorDivide</span>(<span class="py-src-parameter">self</span>):
611 <span class="py-src-variable">self</span>.<span class="py-src-variable">_test_error</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">calc</span>.<span class="py-src-variable">divide</span>)
612 </pre><div class="caption">Source listing - <a href="listings/trial/calculus/test/test_base_3.py"><span class="filename">listings/trial/calculus/test/test_base_3.py</span></a></div></div>
614 <p>We've added four new tests and one general-purpose function, <code class="python">_test_error</code>. This function uses the <code class="python">assertRaises</code> method, which takes an exception class,
615 a function to run and its arguments, and checks that calling the function
616 on the arguments does indeed raise the given exception.</p>
618 <p>If you run the above, you'll see that not all tests fail. In Python it's
619 often valid to add and multiply objects of different and even differing
620 types, so the code in the add and mutiply tests does not raise an exception
621 and those tests therefore fail. So let's add explicit type conversion to
622 our API class. This brings us to <code class="py-filename">calculus/base_3.py</code>:</p>
624 <div class="py-listing"><pre><p class="py-linenumber"> 1
648 </p><span class="py-src-comment"># -*- test-case-name: calculus.test.test_base_3 -*-</span>
650 <span class="py-src-keyword">class</span> <span class="py-src-identifier">Calculation</span>(<span class="py-src-parameter">object</span>):
651 <span class="py-src-keyword">def</span> <span class="py-src-identifier">_make_ints</span>(<span class="py-src-parameter">self</span>, *<span class="py-src-parameter">args</span>):
652 <span class="py-src-keyword">try</span>:
653 <span class="py-src-keyword">return</span> <span class="py-src-variable">map</span>(<span class="py-src-variable">int</span>, <span class="py-src-variable">args</span>)
654 <span class="py-src-keyword">except</span> <span class="py-src-variable">ValueError</span>:
655 <span class="py-src-keyword">raise</span> <span class="py-src-variable">TypeError</span>(<span class="py-src-string">"Couldn't coerce arguments to integers: %s"</span> % <span class="py-src-variable">args</span>)
657 <span class="py-src-keyword">def</span> <span class="py-src-identifier">add</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
658 <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">_make_ints</span>(<span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
659 <span class="py-src-keyword">return</span> <span class="py-src-variable">a</span> + <span class="py-src-variable">b</span>
661 <span class="py-src-keyword">def</span> <span class="py-src-identifier">subtract</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
662 <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">_make_ints</span>(<span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
663 <span class="py-src-keyword">return</span> <span class="py-src-variable">a</span> - <span class="py-src-variable">b</span>
665 <span class="py-src-keyword">def</span> <span class="py-src-identifier">multiply</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
666 <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">_make_ints</span>(<span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
667 <span class="py-src-keyword">return</span> <span class="py-src-variable">a</span> * <span class="py-src-variable">b</span>
669 <span class="py-src-keyword">def</span> <span class="py-src-identifier">divide</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
670 <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">_make_ints</span>(<span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
671 <span class="py-src-keyword">return</span> <span class="py-src-variable">a</span> / <span class="py-src-variable">b</span>
672 </pre><div class="caption">Source listing - <a href="listings/trial/calculus/base_3.py"><span class="filename">listings/trial/calculus/base_3.py</span></a></div></div>
674 <p>Here the <code class="python">_make_ints</code> helper function tries to
675 convert a list into a list of equivalent integers, and raises a <code class="python">TypeError</code> in case the conversion goes wrong.
677 <div class="note"><strong>Note: </strong>The <code class="python">int</code> conversion can also
678 raise a <code class="python">TypeError</code> if passed something of the
679 wrong type, such as a list. We'll just let that exception go by as <code class="python">TypeError</code> is already what we want in case something
680 goes wrong.</div></p>
683 <a name="twisted" shape="rect"/>
684 <h2>Twisted specific testing<a name="auto4"/></h2>
686 <p>Up to this point we've been doing fairly standard Python unit testing.
687 With only a few cosmetic changes (most importantly, directly importing
688 <code class="python">unittest</code> instead of using Twisted's <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.trial.unittest.html" title="twisted.trial.unittest">unittest</a></code> version) we could make the
689 above tests run using Python's standard library unit testing framework.</p>
691 <p>Here we will assume a basic familiarity with Twisted's network I/O, timing,
692 and Deferred APIs. If you haven't already read them, you should read the
693 documentation on <a href="servers.html" shape="rect">Writing
694 Servers</a>, <a href="clients.html" shape="rect">Writing Clients</a>,
695 and <a href="defer.html" shape="rect">Deferreds</a>.</p>
697 <p>Now we'll get to the real point of this tutorial and take advantage of
698 Trial to test Twisted code.</p>
700 <h2>Testing a protocol<a name="auto5"/></h2>
702 <p>We'll now create a custom protocol to invoke our class from within a
703 telnet-like session. We'll remotely call commands with arguments and read back
704 the response. The goal will be to test our network code without creating
707 <h3>Creating and testing the server<a name="auto6"/></h3>
709 <p>First we'll write the tests, and then explain what they do. The first
710 version of the remote test code is:</p>
712 <div class="py-listing"><pre><p class="py-linenumber"> 1
746 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">calculus</span>.<span class="py-src-variable">remote_1</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">RemoteCalculationFactory</span>
747 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">trial</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">unittest</span>
748 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">test</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">proto_helpers</span>
752 <span class="py-src-keyword">class</span> <span class="py-src-identifier">RemoteCalculationTestCase</span>(<span class="py-src-parameter">unittest</span>.<span class="py-src-parameter">TestCase</span>):
753 <span class="py-src-keyword">def</span> <span class="py-src-identifier">setUp</span>(<span class="py-src-parameter">self</span>):
754 <span class="py-src-variable">factory</span> = <span class="py-src-variable">RemoteCalculationFactory</span>()
755 <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span> = <span class="py-src-variable">factory</span>.<span class="py-src-variable">buildProtocol</span>((<span class="py-src-string">'127.0.0.1'</span>, <span class="py-src-number">0</span>))
756 <span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span> = <span class="py-src-variable">proto_helpers</span>.<span class="py-src-variable">StringTransport</span>()
757 <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>.<span class="py-src-variable">makeConnection</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span>)
760 <span class="py-src-keyword">def</span> <span class="py-src-identifier">_test</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">operation</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>, <span class="py-src-parameter">expected</span>):
761 <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>.<span class="py-src-variable">dataReceived</span>(<span class="py-src-string">'%s %d %d\r\n'</span> % (<span class="py-src-variable">operation</span>, <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>))
762 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>(<span class="py-src-variable">int</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span>.<span class="py-src-variable">value</span>()), <span class="py-src-variable">expected</span>)
765 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_add</span>(<span class="py-src-parameter">self</span>):
766 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">'add'</span>, <span class="py-src-number">7</span>, <span class="py-src-number">6</span>, <span class="py-src-number">13</span>)
769 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_subtract</span>(<span class="py-src-parameter">self</span>):
770 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">'subtract'</span>, <span class="py-src-number">82</span>, <span class="py-src-number">78</span>, <span class="py-src-number">4</span>)
773 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_multiply</span>(<span class="py-src-parameter">self</span>):
774 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">'multiply'</span>, <span class="py-src-number">2</span>, <span class="py-src-number">8</span>, <span class="py-src-number">16</span>)
777 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_divide</span>(<span class="py-src-parameter">self</span>):
778 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">'divide'</span>, <span class="py-src-number">14</span>, <span class="py-src-number">3</span>, <span class="py-src-number">4</span>)
779 </pre><div class="caption">Source listing - <a href="listings/trial/calculus/test/test_remote_1.py"><span class="filename">listings/trial/calculus/test/test_remote_1.py</span></a></div></div>
781 <p>To fully understand this client, it helps a lot to be comfortable with
782 the Factory/Protocol/Transport pattern used in Twisted.</p>
784 <p>We first create a protocol factory object. Note that we have yet to see
785 the <code class="python">RemoteCalculationFactory</code> class. It is in
786 <code class="py-filename">calculus/remote_1.py</code> below. We
787 call <code class="python">buildProtocol</code> to ask the factory to build us a
788 protocol object that knows how to talk to our server. We then make a fake
789 network transport, an instance of <code class="python">twisted.test.proto_helpers.StringTransport</code>
790 class (note that test packages are generally not part of Twisted's public API;
791 <code class="python">twisted.test.proto_helpers</code> is an exception). This fake
792 transport is the key to the communications. It is used to emulate a network
793 connection without a network. The address and port passed to <code>buildProtocol</code>
794 are typically used by the factory to choose to immediately deny remote connections; since we're using a fake transport, we can choose any value that will be acceptable to the factory. In this case the factory just ignores the address, so we don't need to pick anything in particular.</p>
796 <p>Testing protocols without the use of real network connections is both simple and recommended when testing Twisted
797 code. Even though there are many tests in Twisted that use the network,
798 most good tests don't. The problem with unit tests and networking is that
799 networks aren't reliable. We cannot know that they will exhibit reasonable
800 behavior all the time. This creates intermittent test failures due to
801 network vagaries. Right now we're trying to test our Twisted code, not
802 network reliability. By setting up and using a fake transport, we can
803 write 100% reliable tests. We can also test network failures in a deterministic manner, another important part of your complete test suite.</p>
805 <p>The final key to understanding this client code is the <code class="python">_test</code> method. The call to <code class="python">dataReceived</code> simulates data arriving on the network
806 transport. But where does it arrive? It's handed to the <code class="python">lineReceived</code> method of the protocol instance (in
807 <code class="py-filename">calculus/remote_1.py</code> below). So the client
808 is essentially tricking the server into thinking it has received the
809 operation and the arguments over the network. The server (once again, see
810 below) hands the work off to its <code class="python">CalculationProxy</code> object which in turn hands it to its
811 <code class="python">Calculation</code> instance. The result is written
812 back via <code class="python">sendLine</code> (into the fake string
813 transport object), and is then immediately available to the client, who
814 fetches it with <code class="python">tr.value()</code> and checks that it
815 has the expected value. So there's quite a lot going on behind the scenes
816 in the two-line <code class="python">_test</code> method above.</p>
818 <p><em>Finally</em>, let's see the implementation of this protocol. Put the
819 following into <code class="py-filename">calculus/remote_1.py</code>:</p>
821 <div class="py-listing"><pre><p class="py-linenumber"> 1
868 </p><span class="py-src-comment"># -*- test-case-name: calculus.test.test_remote_1 -*-</span>
870 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">protocols</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">basic</span>
871 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">protocol</span>
872 <span class="py-src-keyword">from</span> <span class="py-src-variable">calculus</span>.<span class="py-src-variable">base_3</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Calculation</span>
876 <span class="py-src-keyword">class</span> <span class="py-src-identifier">CalculationProxy</span>(<span class="py-src-parameter">object</span>):
877 <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>):
878 <span class="py-src-variable">self</span>.<span class="py-src-variable">calc</span> = <span class="py-src-variable">Calculation</span>()
879 <span class="py-src-keyword">for</span> <span class="py-src-variable">m</span> <span class="py-src-keyword">in</span> [<span class="py-src-string">'add'</span>, <span class="py-src-string">'subtract'</span>, <span class="py-src-string">'multiply'</span>, <span class="py-src-string">'divide'</span>]:
880 <span class="py-src-variable">setattr</span>(<span class="py-src-variable">self</span>, <span class="py-src-string">'remote_%s'</span> % <span class="py-src-variable">m</span>, <span class="py-src-variable">getattr</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">calc</span>, <span class="py-src-variable">m</span>))
884 <span class="py-src-keyword">class</span> <span class="py-src-identifier">RemoteCalculationProtocol</span>(<span class="py-src-parameter">basic</span>.<span class="py-src-parameter">LineReceiver</span>):
885 <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>):
886 <span class="py-src-variable">self</span>.<span class="py-src-variable">proxy</span> = <span class="py-src-variable">CalculationProxy</span>()
889 <span class="py-src-keyword">def</span> <span class="py-src-identifier">lineReceived</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">line</span>):
890 <span class="py-src-variable">op</span>, <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span> = <span class="py-src-variable">line</span>.<span class="py-src-variable">split</span>()
891 <span class="py-src-variable">a</span> = <span class="py-src-variable">int</span>(<span class="py-src-variable">a</span>)
892 <span class="py-src-variable">b</span> = <span class="py-src-variable">int</span>(<span class="py-src-variable">b</span>)
893 <span class="py-src-variable">op</span> = <span class="py-src-variable">getattr</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">proxy</span>, <span class="py-src-string">'remote_%s'</span> % (<span class="py-src-variable">op</span>,))
894 <span class="py-src-variable">result</span> = <span class="py-src-variable">op</span>(<span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
895 <span class="py-src-variable">self</span>.<span class="py-src-variable">sendLine</span>(<span class="py-src-variable">str</span>(<span class="py-src-variable">result</span>))
899 <span class="py-src-keyword">class</span> <span class="py-src-identifier">RemoteCalculationFactory</span>(<span class="py-src-parameter">protocol</span>.<span class="py-src-parameter">Factory</span>):
900 <span class="py-src-variable">protocol</span> = <span class="py-src-variable">RemoteCalculationProtocol</span>
904 <span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span>():
905 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>
906 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">python</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">log</span>
907 <span class="py-src-keyword">import</span> <span class="py-src-variable">sys</span>
908 <span class="py-src-variable">log</span>.<span class="py-src-variable">startLogging</span>(<span class="py-src-variable">sys</span>.<span class="py-src-variable">stdout</span>)
909 <span class="py-src-variable">reactor</span>.<span class="py-src-variable">listenTCP</span>(<span class="py-src-number">0</span>, <span class="py-src-variable">RemoteCalculationFactory</span>())
910 <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
913 <span class="py-src-keyword">if</span> <span class="py-src-variable">__name__</span> == <span class="py-src-string">"__main__"</span>:
914 <span class="py-src-variable">main</span>()
915 </pre><div class="caption">Source listing - <a href="listings/trial/calculus/remote_1.py"><span class="filename">listings/trial/calculus/remote_1.py</span></a></div></div>
917 <p>As mentioned, this server creates a protocol that inherits from <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.protocols.basic.LineReceiver.html" title="twisted.protocols.basic.LineReceiver">basic.LineReceiver</a></code>, and then a
918 factory that uses it as protocol. The only trick is the <code class="python">CalculationProxy</code> object, which calls <code class="python">Calculation</code> methods through <code class="python">remote_*</code> methods. This pattern is used frequently in
919 Twisted, because it is very explicit about what methods you are making
922 <p>If you run this test (<code class="shell">trial
923 calculus.test.test_remote_1</code>), everything should be fine. You can also
924 run a server to test it with a telnet client. To do that, call <code class="shell">python calculus/remote_1.py</code>. You should have the following output:</p>
926 <pre class="shell" xml:space="preserve">
927 2008-04-25 10:53:27+0200 [-] Log opened.
928 2008-04-25 10:53:27+0200 [-] __main__.RemoteCalculationFactory starting on 46194
929 2008-04-25 10:53:27+0200 [-] Starting factory <__main__.RemoteCalculationFactory instance at 0x846a0cc>
932 <p>46194 is replaced by a random port. You can then call telnet on it:</p>
933 <pre xml:space="preserve">
934 $ telnet localhost 46194
936 Connected to localhost.
937 Escape character is '^]'.
944 <h3>Creating and testing the client<a name="auto7"/></h3>
946 <p>Of course, what we build is not particulary useful for now : we'll now build
947 a client to our server, to be able to use it inside a Python program. And it
948 will serve our next purpose.</p>
950 <p>Create <code class="py-filename">calculus/test/test_client_1.py</code>:</p>
952 <div class="py-listing"><pre><p class="py-linenumber"> 1
989 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">calculus</span>.<span class="py-src-variable">client_1</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">RemoteCalculationClient</span>
990 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">trial</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">unittest</span>
991 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">test</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">proto_helpers</span>
995 <span class="py-src-keyword">class</span> <span class="py-src-identifier">ClientCalculationTestCase</span>(<span class="py-src-parameter">unittest</span>.<span class="py-src-parameter">TestCase</span>):
996 <span class="py-src-keyword">def</span> <span class="py-src-identifier">setUp</span>(<span class="py-src-parameter">self</span>):
997 <span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span> = <span class="py-src-variable">proto_helpers</span>.<span class="py-src-variable">StringTransport</span>()
998 <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span> = <span class="py-src-variable">RemoteCalculationClient</span>()
999 <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>.<span class="py-src-variable">makeConnection</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span>)
1002 <span class="py-src-keyword">def</span> <span class="py-src-identifier">_test</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">operation</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>, <span class="py-src-parameter">expected</span>):
1003 <span class="py-src-variable">d</span> = <span class="py-src-variable">getattr</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>, <span class="py-src-variable">operation</span>)(<span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
1004 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span>.<span class="py-src-variable">value</span>(), <span class="py-src-string">'%s %d %d\r\n'</span> % (<span class="py-src-variable">operation</span>, <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>))
1005 <span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span>.<span class="py-src-variable">clear</span>()
1006 <span class="py-src-variable">d</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>, <span class="py-src-variable">expected</span>)
1007 <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>.<span class="py-src-variable">dataReceived</span>(<span class="py-src-string">"%d\r\n"</span> % (<span class="py-src-variable">expected</span>,))
1008 <span class="py-src-keyword">return</span> <span class="py-src-variable">d</span>
1011 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_add</span>(<span class="py-src-parameter">self</span>):
1012 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">'add'</span>, <span class="py-src-number">7</span>, <span class="py-src-number">6</span>, <span class="py-src-number">13</span>)
1015 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_subtract</span>(<span class="py-src-parameter">self</span>):
1016 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">'subtract'</span>, <span class="py-src-number">82</span>, <span class="py-src-number">78</span>, <span class="py-src-number">4</span>)
1019 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_multiply</span>(<span class="py-src-parameter">self</span>):
1020 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">'multiply'</span>, <span class="py-src-number">2</span>, <span class="py-src-number">8</span>, <span class="py-src-number">16</span>)
1023 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_divide</span>(<span class="py-src-parameter">self</span>):
1024 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">'divide'</span>, <span class="py-src-number">14</span>, <span class="py-src-number">3</span>, <span class="py-src-number">4</span>)
1025 </pre><div class="caption">Source listing - <a href="listings/trial/calculus/test/test_client_1.py"><span class="filename">listings/trial/calculus/test/test_client_1.py</span></a></div></div>
1027 <p>It's really symmetric to the server test cases. The only tricky part is
1028 that we don't use a client factory. We're lazy, and it's not very useful in
1029 the client part, so we instantiate the protocol directly.</p>
1031 <p>Incidentally, we have introduced a very important concept here: the tests
1032 now return a Deferred object, and the assertion is done in a callback. The
1033 important thing to do here is to <strong>not forget to return the
1034 Deferred</strong>. If you do, your tests will pass even if nothing is asserted.
1035 That's also why it's important to make tests fail first: if your tests pass
1036 whereas you know they shouldn't, there is a problem in your tests.</p>
1038 <p>We'll now add the remote client class to produce <code class="py-filename">calculus/client_1.py</code>:</p>
1040 <div class="py-listing"><pre><p class="py-linenumber"> 1
1079 </p><span class="py-src-comment"># -*- test-case-name: calculus.test.test_client_1 -*-</span>
1081 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">protocols</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">basic</span>
1082 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">defer</span>
1086 <span class="py-src-keyword">class</span> <span class="py-src-identifier">RemoteCalculationClient</span>(<span class="py-src-parameter">basic</span>.<span class="py-src-parameter">LineReceiver</span>):
1087 <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>):
1088 <span class="py-src-variable">self</span>.<span class="py-src-variable">results</span> = []
1091 <span class="py-src-keyword">def</span> <span class="py-src-identifier">lineReceived</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">line</span>):
1092 <span class="py-src-variable">d</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">results</span>.<span class="py-src-variable">pop</span>(<span class="py-src-number">0</span>)
1093 <span class="py-src-variable">d</span>.<span class="py-src-variable">callback</span>(<span class="py-src-variable">int</span>(<span class="py-src-variable">line</span>))
1096 <span class="py-src-keyword">def</span> <span class="py-src-identifier">_sendOperation</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">op</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
1097 <span class="py-src-variable">d</span> = <span class="py-src-variable">defer</span>.<span class="py-src-variable">Deferred</span>()
1098 <span class="py-src-variable">self</span>.<span class="py-src-variable">results</span>.<span class="py-src-variable">append</span>(<span class="py-src-variable">d</span>)
1099 <span class="py-src-variable">line</span> = <span class="py-src-string">"%s %d %d"</span> % (<span class="py-src-variable">op</span>, <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
1100 <span class="py-src-variable">self</span>.<span class="py-src-variable">sendLine</span>(<span class="py-src-variable">line</span>)
1101 <span class="py-src-keyword">return</span> <span class="py-src-variable">d</span>
1104 <span class="py-src-keyword">def</span> <span class="py-src-identifier">add</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
1105 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_sendOperation</span>(<span class="py-src-string">"add"</span>, <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
1108 <span class="py-src-keyword">def</span> <span class="py-src-identifier">subtract</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
1109 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_sendOperation</span>(<span class="py-src-string">"subtract"</span>, <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
1112 <span class="py-src-keyword">def</span> <span class="py-src-identifier">multiply</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
1113 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_sendOperation</span>(<span class="py-src-string">"multiply"</span>, <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
1116 <span class="py-src-keyword">def</span> <span class="py-src-identifier">divide</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
1117 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_sendOperation</span>(<span class="py-src-string">"divide"</span>, <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
1118 </pre><div class="caption">Source listing - <a href="listings/trial/calculus/client_1.py"><span class="filename">listings/trial/calculus/client_1.py</span></a></div></div>
1121 <h2>More good practices<a name="auto8"/></h2>
1123 <h3>Testing scheduling<a name="auto9"/></h3>
1125 <p>When testing code that involves the passage of time, waiting e.g. for a two hour timeout to occur in a test is not very realistic. Twisted provides a solution to this, the <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.internet.task.Clock.html" title="twisted.internet.task.Clock">Clock</a></code> class that allows one to simulate the passage of time.</p>
1127 <p>As an example we'll test the code for client request timeout: since our client
1128 uses TCP it can hang for a long time (firewall, connectivity problems, etc...).
1129 So generally we need to implement timeouts on the client side. Basically it's
1130 just that we send a request, don't receive a response and expect a timeout error
1131 to be triggered after a certain duration.
1134 <div class="py-listing"><pre><p class="py-linenumber"> 1
1182 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">calculus</span>.<span class="py-src-variable">client_2</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">RemoteCalculationClient</span>, <span class="py-src-variable">ClientTimeoutError</span>
1184 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">task</span>
1185 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">trial</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">unittest</span>
1186 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">test</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">proto_helpers</span>
1190 <span class="py-src-keyword">class</span> <span class="py-src-identifier">ClientCalculationTestCase</span>(<span class="py-src-parameter">unittest</span>.<span class="py-src-parameter">TestCase</span>):
1191 <span class="py-src-keyword">def</span> <span class="py-src-identifier">setUp</span>(<span class="py-src-parameter">self</span>):
1192 <span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span> = <span class="py-src-variable">proto_helpers</span>.<span class="py-src-variable">StringTransportWithDisconnection</span>()
1193 <span class="py-src-variable">self</span>.<span class="py-src-variable">clock</span> = <span class="py-src-variable">task</span>.<span class="py-src-variable">Clock</span>()
1194 <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span> = <span class="py-src-variable">RemoteCalculationClient</span>()
1195 <span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span>.<span class="py-src-variable">protocol</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>
1196 <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>.<span class="py-src-variable">callLater</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">clock</span>.<span class="py-src-variable">callLater</span>
1197 <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>.<span class="py-src-variable">makeConnection</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span>)
1200 <span class="py-src-keyword">def</span> <span class="py-src-identifier">_test</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">operation</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>, <span class="py-src-parameter">expected</span>):
1201 <span class="py-src-variable">d</span> = <span class="py-src-variable">getattr</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>, <span class="py-src-variable">operation</span>)(<span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
1202 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span>.<span class="py-src-variable">value</span>(), <span class="py-src-string">'%s %d %d\r\n'</span> % (<span class="py-src-variable">operation</span>, <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>))
1203 <span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span>.<span class="py-src-variable">clear</span>()
1204 <span class="py-src-variable">d</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>, <span class="py-src-variable">expected</span>)
1205 <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>.<span class="py-src-variable">dataReceived</span>(<span class="py-src-string">"%d\r\n"</span> % (<span class="py-src-variable">expected</span>,))
1206 <span class="py-src-keyword">return</span> <span class="py-src-variable">d</span>
1209 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_add</span>(<span class="py-src-parameter">self</span>):
1210 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">'add'</span>, <span class="py-src-number">7</span>, <span class="py-src-number">6</span>, <span class="py-src-number">13</span>)
1213 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_subtract</span>(<span class="py-src-parameter">self</span>):
1214 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">'subtract'</span>, <span class="py-src-number">82</span>, <span class="py-src-number">78</span>, <span class="py-src-number">4</span>)
1217 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_multiply</span>(<span class="py-src-parameter">self</span>):
1218 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">'multiply'</span>, <span class="py-src-number">2</span>, <span class="py-src-number">8</span>, <span class="py-src-number">16</span>)
1221 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_divide</span>(<span class="py-src-parameter">self</span>):
1222 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">'divide'</span>, <span class="py-src-number">14</span>, <span class="py-src-number">3</span>, <span class="py-src-number">4</span>)
1225 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_timeout</span>(<span class="py-src-parameter">self</span>):
1226 <span class="py-src-variable">d</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>.<span class="py-src-variable">add</span>(<span class="py-src-number">9</span>, <span class="py-src-number">4</span>)
1227 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span>.<span class="py-src-variable">value</span>(), <span class="py-src-string">'add 9 4\r\n'</span>)
1228 <span class="py-src-variable">self</span>.<span class="py-src-variable">clock</span>.<span class="py-src-variable">advance</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>.<span class="py-src-variable">timeOut</span>)
1229 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">assertFailure</span>(<span class="py-src-variable">d</span>, <span class="py-src-variable">ClientTimeoutError</span>)
1230 </pre><div class="caption">Source listing - <a href="listings/trial/calculus/test/test_client_2.py"><span class="filename">listings/trial/calculus/test/test_client_2.py</span></a></div></div>
1232 <p>What happens here? We instantiate our protocol as usual, the only trick
1233 is to create the clock, and assign <code class="python">proto.callLater</code> to
1234 <code class="python">clock.callLater</code>. Thus, every callLater calls in the protocol
1235 will finish before <code class="python">clock.advance()</code> returns.</p>
1237 <p>In the new test (test_timeout), we call <code class="python">clock.advance</code>, that simulates and advance in time
1238 (logically it's similar to a <code class="python">time.sleep</code> call). And
1239 we just have to verify that our Deferred got a timeout error.</p>
1241 <p>Let's implement that in our code.</p>
1243 <div class="py-listing"><pre><p class="py-linenumber"> 1
1297 </p><span class="py-src-comment"># -*- test-case-name: calculus.test.test_client_2 -*-</span>
1299 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">protocols</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">basic</span>
1300 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">defer</span>, <span class="py-src-variable">reactor</span>
1304 <span class="py-src-keyword">class</span> <span class="py-src-identifier">ClientTimeoutError</span>(<span class="py-src-parameter">Exception</span>):
1305 <span class="py-src-keyword">pass</span>
1309 <span class="py-src-keyword">class</span> <span class="py-src-identifier">RemoteCalculationClient</span>(<span class="py-src-parameter">basic</span>.<span class="py-src-parameter">LineReceiver</span>):
1311 <span class="py-src-variable">callLater</span> = <span class="py-src-variable">reactor</span>.<span class="py-src-variable">callLater</span>
1312 <span class="py-src-variable">timeOut</span> = <span class="py-src-number">60</span>
1314 <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>):
1315 <span class="py-src-variable">self</span>.<span class="py-src-variable">results</span> = []
1318 <span class="py-src-keyword">def</span> <span class="py-src-identifier">lineReceived</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">line</span>):
1319 <span class="py-src-variable">d</span>, <span class="py-src-variable">callID</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">results</span>.<span class="py-src-variable">pop</span>(<span class="py-src-number">0</span>)
1320 <span class="py-src-variable">callID</span>.<span class="py-src-variable">cancel</span>()
1321 <span class="py-src-variable">d</span>.<span class="py-src-variable">callback</span>(<span class="py-src-variable">int</span>(<span class="py-src-variable">line</span>))
1324 <span class="py-src-keyword">def</span> <span class="py-src-identifier">_cancel</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">d</span>):
1325 <span class="py-src-variable">d</span>.<span class="py-src-variable">errback</span>(<span class="py-src-variable">ClientTimeoutError</span>())
1328 <span class="py-src-keyword">def</span> <span class="py-src-identifier">_sendOperation</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">op</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
1329 <span class="py-src-variable">d</span> = <span class="py-src-variable">defer</span>.<span class="py-src-variable">Deferred</span>()
1330 <span class="py-src-variable">callID</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">callLater</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">timeOut</span>, <span class="py-src-variable">self</span>.<span class="py-src-variable">_cancel</span>, <span class="py-src-variable">d</span>)
1331 <span class="py-src-variable">self</span>.<span class="py-src-variable">results</span>.<span class="py-src-variable">append</span>((<span class="py-src-variable">d</span>, <span class="py-src-variable">callID</span>))
1332 <span class="py-src-variable">line</span> = <span class="py-src-string">"%s %d %d"</span> % (<span class="py-src-variable">op</span>, <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
1333 <span class="py-src-variable">self</span>.<span class="py-src-variable">sendLine</span>(<span class="py-src-variable">line</span>)
1334 <span class="py-src-keyword">return</span> <span class="py-src-variable">d</span>
1337 <span class="py-src-keyword">def</span> <span class="py-src-identifier">add</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
1338 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_sendOperation</span>(<span class="py-src-string">"add"</span>, <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
1341 <span class="py-src-keyword">def</span> <span class="py-src-identifier">subtract</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
1342 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_sendOperation</span>(<span class="py-src-string">"subtract"</span>, <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
1345 <span class="py-src-keyword">def</span> <span class="py-src-identifier">multiply</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
1346 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_sendOperation</span>(<span class="py-src-string">"multiply"</span>, <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
1349 <span class="py-src-keyword">def</span> <span class="py-src-identifier">divide</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
1350 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_sendOperation</span>(<span class="py-src-string">"divide"</span>, <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
1351 </pre><div class="caption">Source listing - <a href="listings/trial/calculus/client_2.py"><span class="filename">listings/trial/calculus/client_2.py</span></a></div></div>
1353 <p>The only important thing here is to not forget to cancel our callLater
1354 when everything went fine.</p>
1356 <h3>Cleaning up after tests<a name="auto10"/></h3>
1358 <p>This chapter is mainly intended for people that want to have sockets or
1359 processes created in their tests. If it's still not obvious, you must try to
1360 avoid that like the plague, because it ends up with a lot of problems, one of
1361 them being intermittent failures. And intermittent failures are the plague
1362 of automated tests.</p>
1364 <p>To actually test that, we'll launch a server with our protocol.</p>
1366 <div class="py-listing"><pre><p class="py-linenumber"> 1
1412 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">calculus</span>.<span class="py-src-variable">remote_1</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">RemoteCalculationFactory</span>
1413 <span class="py-src-keyword">from</span> <span class="py-src-variable">calculus</span>.<span class="py-src-variable">client_2</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">RemoteCalculationClient</span>
1415 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">trial</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">unittest</span>
1416 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>, <span class="py-src-variable">protocol</span>
1420 <span class="py-src-keyword">class</span> <span class="py-src-identifier">RemoteRunCalculationTestCase</span>(<span class="py-src-parameter">unittest</span>.<span class="py-src-parameter">TestCase</span>):
1422 <span class="py-src-keyword">def</span> <span class="py-src-identifier">setUp</span>(<span class="py-src-parameter">self</span>):
1423 <span class="py-src-variable">factory</span> = <span class="py-src-variable">RemoteCalculationFactory</span>()
1424 <span class="py-src-variable">self</span>.<span class="py-src-variable">port</span> = <span class="py-src-variable">reactor</span>.<span class="py-src-variable">listenTCP</span>(<span class="py-src-number">0</span>, <span class="py-src-variable">factory</span>, <span class="py-src-variable">interface</span>=<span class="py-src-string">"127.0.0.1"</span>)
1425 <span class="py-src-variable">self</span>.<span class="py-src-variable">client</span> = <span class="py-src-variable">None</span>
1428 <span class="py-src-keyword">def</span> <span class="py-src-identifier">tearDown</span>(<span class="py-src-parameter">self</span>):
1429 <span class="py-src-keyword">if</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">client</span> <span class="py-src-keyword">is</span> <span class="py-src-keyword">not</span> <span class="py-src-variable">None</span>:
1430 <span class="py-src-variable">self</span>.<span class="py-src-variable">client</span>.<span class="py-src-variable">transport</span>.<span class="py-src-variable">loseConnection</span>()
1431 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">port</span>.<span class="py-src-variable">stopListening</span>()
1434 <span class="py-src-keyword">def</span> <span class="py-src-identifier">_test</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">op</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>, <span class="py-src-parameter">expected</span>):
1435 <span class="py-src-variable">creator</span> = <span class="py-src-variable">protocol</span>.<span class="py-src-variable">ClientCreator</span>(<span class="py-src-variable">reactor</span>, <span class="py-src-variable">RemoteCalculationClient</span>)
1436 <span class="py-src-keyword">def</span> <span class="py-src-identifier">cb</span>(<span class="py-src-parameter">client</span>):
1437 <span class="py-src-variable">self</span>.<span class="py-src-variable">client</span> = <span class="py-src-variable">client</span>
1438 <span class="py-src-keyword">return</span> <span class="py-src-variable">getattr</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">client</span>, <span class="py-src-variable">op</span>)(<span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>
1439 ).<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>, <span class="py-src-variable">expected</span>)
1440 <span class="py-src-keyword">return</span> <span class="py-src-variable">creator</span>.<span class="py-src-variable">connectTCP</span>(<span class="py-src-string">'127.0.0.1'</span>, <span class="py-src-variable">self</span>.<span class="py-src-variable">port</span>.<span class="py-src-variable">getHost</span>().<span class="py-src-variable">port</span>
1441 ).<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">cb</span>)
1444 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_add</span>(<span class="py-src-parameter">self</span>):
1445 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">"add"</span>, <span class="py-src-number">5</span>, <span class="py-src-number">9</span>, <span class="py-src-number">14</span>)
1448 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_subtract</span>(<span class="py-src-parameter">self</span>):
1449 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">"subtract"</span>, <span class="py-src-number">47</span>, <span class="py-src-number">13</span>, <span class="py-src-number">34</span>)
1452 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_multiply</span>(<span class="py-src-parameter">self</span>):
1453 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">"multiply"</span>, <span class="py-src-number">7</span>, <span class="py-src-number">3</span>, <span class="py-src-number">21</span>)
1456 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_divide</span>(<span class="py-src-parameter">self</span>):
1457 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">"divide"</span>, <span class="py-src-number">84</span>, <span class="py-src-number">10</span>, <span class="py-src-number">8</span>)
1458 </pre><div class="caption">Source listing - <a href="listings/trial/calculus/test/test_remote_2.py"><span class="filename">listings/trial/calculus/test/test_remote_2.py</span></a></div></div>
1460 <p>Recent versions of trial will fail loudly if you remove the
1461 <code class="python">stopListening</code> call, which is good.</p>
1463 <p>Also, you should be aware that <code class="python">tearDown</code> will
1464 called in any case, after success or failure. So don't expect that every
1465 objects you created in the test method are present, because your tests may
1466 have failed in the middle.</p>
1468 <p>Trial also has a <code class="python">addCleanup</code> method, which makes
1469 these kind of cleanups easy and removes the need for <code class="python">tearDown
1470 </code>. For example, you could remove the code in <code class="python">_test</code>
1473 <pre class="python"><p class="py-linenumber"> 1
1484 </p><span class="py-src-keyword">def</span> <span class="py-src-identifier">setUp</span>(<span class="py-src-parameter">self</span>):
1485 <span class="py-src-variable">factory</span> = <span class="py-src-variable">RemoteCalculationFactory</span>()
1486 <span class="py-src-variable">self</span>.<span class="py-src-variable">port</span> = <span class="py-src-variable">reactor</span>.<span class="py-src-variable">listenTCP</span>(<span class="py-src-number">0</span>, <span class="py-src-variable">factory</span>, <span class="py-src-variable">interface</span>=<span class="py-src-string">"127.0.0.1"</span>)
1487 <span class="py-src-variable">self</span>.<span class="py-src-variable">addCleanup</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">port</span>.<span class="py-src-variable">stopListening</span>)
1489 <span class="py-src-keyword">def</span> <span class="py-src-identifier">_test</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">op</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>, <span class="py-src-parameter">expected</span>):
1490 <span class="py-src-variable">creator</span> = <span class="py-src-variable">protocol</span>.<span class="py-src-variable">ClientCreator</span>(<span class="py-src-variable">reactor</span>, <span class="py-src-variable">RemoteCalculationClient</span>)
1491 <span class="py-src-keyword">def</span> <span class="py-src-identifier">cb</span>(<span class="py-src-parameter">client</span>):
1492 <span class="py-src-variable">self</span>.<span class="py-src-variable">addCleanup</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">client</span>.<span class="py-src-variable">transport</span>.<span class="py-src-variable">loseConnection</span>)
1493 <span class="py-src-keyword">return</span> <span class="py-src-variable">getattr</span>(<span class="py-src-variable">client</span>, <span class="py-src-variable">op</span>)(<span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>).<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>, <span class="py-src-variable">expected</span>)
1494 <span class="py-src-keyword">return</span> <span class="py-src-variable">creator</span>.<span class="py-src-variable">connectTCP</span>(<span class="py-src-string">'127.0.0.1'</span>, <span class="py-src-variable">self</span>.<span class="py-src-variable">port</span>.<span class="py-src-variable">getHost</span>().<span class="py-src-variable">port</span>).<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">cb</span>)
1497 <p>This remove the need of a tearDown method, and you don't have to check for
1498 the value of self.client: you only call addCleanup when the client is
1501 <h3>Handling logged errors<a name="auto11"/></h3>
1503 <p>Currently, if you send an invalid command or invalid arguments to our
1504 server, it logs an exception and closes the connection. This is a perfectly
1505 valid behavior, but for the sake of this tutorial, we want to return an error
1506 to the user if he sends invalid operators, and log any errors on server side.
1507 So we'll want a test like this:</p>
1509 <pre class="python"><p class="py-linenumber">1
1512 </p><span class="py-src-keyword">def</span> <span class="py-src-identifier">test_invalidParameters</span>(<span class="py-src-parameter">self</span>):
1513 <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>.<span class="py-src-variable">dataReceived</span>(<span class="py-src-string">'add foo bar\r\n'</span>)
1514 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span>.<span class="py-src-variable">value</span>(), <span class="py-src-string">"error\r\n"</span>)
1517 <div class="py-listing"><pre><p class="py-linenumber"> 1
1568 </p><span class="py-src-comment"># -*- test-case-name: calculus.test.test_remote_1 -*-</span>
1570 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">protocols</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">basic</span>
1571 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">protocol</span>
1572 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">python</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">log</span>
1573 <span class="py-src-keyword">from</span> <span class="py-src-variable">calculus</span>.<span class="py-src-variable">base_3</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Calculation</span>
1577 <span class="py-src-keyword">class</span> <span class="py-src-identifier">CalculationProxy</span>(<span class="py-src-parameter">object</span>):
1578 <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>):
1579 <span class="py-src-variable">self</span>.<span class="py-src-variable">calc</span> = <span class="py-src-variable">Calculation</span>()
1580 <span class="py-src-keyword">for</span> <span class="py-src-variable">m</span> <span class="py-src-keyword">in</span> [<span class="py-src-string">'add'</span>, <span class="py-src-string">'subtract'</span>, <span class="py-src-string">'multiply'</span>, <span class="py-src-string">'divide'</span>]:
1581 <span class="py-src-variable">setattr</span>(<span class="py-src-variable">self</span>, <span class="py-src-string">'remote_%s'</span> % <span class="py-src-variable">m</span>, <span class="py-src-variable">getattr</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">calc</span>, <span class="py-src-variable">m</span>))
1585 <span class="py-src-keyword">class</span> <span class="py-src-identifier">RemoteCalculationProtocol</span>(<span class="py-src-parameter">basic</span>.<span class="py-src-parameter">LineReceiver</span>):
1586 <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>):
1587 <span class="py-src-variable">self</span>.<span class="py-src-variable">proxy</span> = <span class="py-src-variable">CalculationProxy</span>()
1590 <span class="py-src-keyword">def</span> <span class="py-src-identifier">lineReceived</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">line</span>):
1591 <span class="py-src-variable">op</span>, <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span> = <span class="py-src-variable">line</span>.<span class="py-src-variable">split</span>()
1592 <span class="py-src-variable">op</span> = <span class="py-src-variable">getattr</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">proxy</span>, <span class="py-src-string">'remote_%s'</span> % (<span class="py-src-variable">op</span>,))
1593 <span class="py-src-keyword">try</span>:
1594 <span class="py-src-variable">result</span> = <span class="py-src-variable">op</span>(<span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
1595 <span class="py-src-keyword">except</span> <span class="py-src-variable">TypeError</span>:
1596 <span class="py-src-variable">log</span>.<span class="py-src-variable">err</span>()
1597 <span class="py-src-variable">self</span>.<span class="py-src-variable">sendLine</span>(<span class="py-src-string">"error"</span>)
1598 <span class="py-src-keyword">else</span>:
1599 <span class="py-src-variable">self</span>.<span class="py-src-variable">sendLine</span>(<span class="py-src-variable">str</span>(<span class="py-src-variable">result</span>))
1603 <span class="py-src-keyword">class</span> <span class="py-src-identifier">RemoteCalculationFactory</span>(<span class="py-src-parameter">protocol</span>.<span class="py-src-parameter">Factory</span>):
1604 <span class="py-src-variable">protocol</span> = <span class="py-src-variable">RemoteCalculationProtocol</span>
1608 <span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span>():
1609 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>
1610 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">python</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">log</span>
1611 <span class="py-src-keyword">import</span> <span class="py-src-variable">sys</span>
1612 <span class="py-src-variable">log</span>.<span class="py-src-variable">startLogging</span>(<span class="py-src-variable">sys</span>.<span class="py-src-variable">stdout</span>)
1613 <span class="py-src-variable">reactor</span>.<span class="py-src-variable">listenTCP</span>(<span class="py-src-number">0</span>, <span class="py-src-variable">RemoteCalculationFactory</span>())
1614 <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
1617 <span class="py-src-keyword">if</span> <span class="py-src-variable">__name__</span> == <span class="py-src-string">"__main__"</span>:
1618 <span class="py-src-variable">main</span>()
1619 </pre><div class="caption">Source listing - <a href="listings/trial/calculus/remote_2.py"><span class="filename">listings/trial/calculus/remote_2.py</span></a></div></div>
1621 <p>If you try something like that, it will not work. Here is the output you should have:</p>
1623 <pre class="shell" xml:space="preserve">
1624 trial calculus.test.test_remote_3.RemoteCalculationTestCase.test_invalidParameters
1625 calculus.test.test_remote_3
1626 RemoteCalculationTestCase
1627 test_invalidParameters ... [ERROR]
1629 ===============================================================================
1630 [ERROR]: calculus.test.test_remote_3.RemoteCalculationTestCase.test_invalidParameters
1632 Traceback (most recent call last):
1633 File "/tmp/calculus/remote_2.py", line 27, in lineReceived
1635 File "/tmp/calculus/base_3.py", line 11, in add
1636 a, b = self._make_ints(a, b)
1637 File "/tmp/calculus/base_3.py", line 8, in _make_ints
1639 exceptions.TypeError:
1640 -------------------------------------------------------------------------------
1641 Ran 1 tests in 0.004s
1646 <p>At first, you could think there is a problem, because you catch this
1647 exception. But in fact trial doesn't let you do that without controlling it:
1648 you must expect logged errors and clean them. To do that, you have to use the
1649 <code class="python">flushLoggedErrors</code> method. You call it with the
1650 exception you expect, and it returns the list of exceptions logged since the
1651 start of the test. Generally, you'll want to check that this list has the
1652 expected length, or possibly that each exception has an expected message. We do
1653 the former in our test:</p>
1655 <div class="py-listing"><pre><p class="py-linenumber"> 1
1695 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">calculus</span>.<span class="py-src-variable">remote_2</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">RemoteCalculationFactory</span>
1696 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">trial</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">unittest</span>
1697 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">test</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">proto_helpers</span>
1701 <span class="py-src-keyword">class</span> <span class="py-src-identifier">RemoteCalculationTestCase</span>(<span class="py-src-parameter">unittest</span>.<span class="py-src-parameter">TestCase</span>):
1702 <span class="py-src-keyword">def</span> <span class="py-src-identifier">setUp</span>(<span class="py-src-parameter">self</span>):
1703 <span class="py-src-variable">factory</span> = <span class="py-src-variable">RemoteCalculationFactory</span>()
1704 <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span> = <span class="py-src-variable">factory</span>.<span class="py-src-variable">buildProtocol</span>((<span class="py-src-string">'127.0.0.1'</span>, <span class="py-src-number">0</span>))
1705 <span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span> = <span class="py-src-variable">proto_helpers</span>.<span class="py-src-variable">StringTransport</span>()
1706 <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>.<span class="py-src-variable">makeConnection</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span>)
1709 <span class="py-src-keyword">def</span> <span class="py-src-identifier">_test</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">operation</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>, <span class="py-src-parameter">expected</span>):
1710 <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>.<span class="py-src-variable">dataReceived</span>(<span class="py-src-string">'%s %d %d\r\n'</span> % (<span class="py-src-variable">operation</span>, <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>))
1711 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>(<span class="py-src-variable">int</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span>.<span class="py-src-variable">value</span>()), <span class="py-src-variable">expected</span>)
1714 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_add</span>(<span class="py-src-parameter">self</span>):
1715 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">'add'</span>, <span class="py-src-number">7</span>, <span class="py-src-number">6</span>, <span class="py-src-number">13</span>)
1718 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_subtract</span>(<span class="py-src-parameter">self</span>):
1719 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">'subtract'</span>, <span class="py-src-number">82</span>, <span class="py-src-number">78</span>, <span class="py-src-number">4</span>)
1722 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_multiply</span>(<span class="py-src-parameter">self</span>):
1723 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">'multiply'</span>, <span class="py-src-number">2</span>, <span class="py-src-number">8</span>, <span class="py-src-number">16</span>)
1726 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_divide</span>(<span class="py-src-parameter">self</span>):
1727 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">'divide'</span>, <span class="py-src-number">14</span>, <span class="py-src-number">3</span>, <span class="py-src-number">4</span>)
1730 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_invalidParameters</span>(<span class="py-src-parameter">self</span>):
1731 <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>.<span class="py-src-variable">dataReceived</span>(<span class="py-src-string">'add foo bar\r\n'</span>)
1732 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span>.<span class="py-src-variable">value</span>(), <span class="py-src-string">"error\r\n"</span>)
1733 <span class="py-src-variable">errors</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">flushLoggedErrors</span>(<span class="py-src-variable">TypeError</span>)
1734 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>(<span class="py-src-variable">len</span>(<span class="py-src-variable">errors</span>), <span class="py-src-number">1</span>)
1735 </pre><div class="caption">Source listing - <a href="listings/trial/calculus/test/test_remote_3.py"><span class="filename">listings/trial/calculus/test/test_remote_3.py</span></a></div></div>
1737 <h2>Resolve a bug<a name="auto12"/></h2>
1739 <p>A bug was left over during the development of the timeout (probably several
1740 bugs, but that's not the point), concerning the reuse of the protocol when you
1741 got a timeout: the connection is not dropped, so you can get timeout forever.
1742 Generally an user will come to you saying "I have this strange problem on
1743 my crappy network environment. It seems you could solve it with doing XXX at
1746 <p>Actually, this bug can be corrected several ways. But if you correct it
1747 without adding tests, one day you'll face a big problem: regression.
1748 So the first step is adding a failing test.</p>
1750 <div class="py-listing"><pre><p class="py-linenumber"> 1
1813 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">calculus</span>.<span class="py-src-variable">client_3</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">RemoteCalculationClient</span>, <span class="py-src-variable">ClientTimeoutError</span>
1815 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">task</span>
1816 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">trial</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">unittest</span>
1817 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">test</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">proto_helpers</span>
1821 <span class="py-src-keyword">class</span> <span class="py-src-identifier">ClientCalculationTestCase</span>(<span class="py-src-parameter">unittest</span>.<span class="py-src-parameter">TestCase</span>):
1822 <span class="py-src-keyword">def</span> <span class="py-src-identifier">setUp</span>(<span class="py-src-parameter">self</span>):
1823 <span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span> = <span class="py-src-variable">proto_helpers</span>.<span class="py-src-variable">StringTransportWithDisconnection</span>()
1824 <span class="py-src-variable">self</span>.<span class="py-src-variable">clock</span> = <span class="py-src-variable">task</span>.<span class="py-src-variable">Clock</span>()
1825 <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span> = <span class="py-src-variable">RemoteCalculationClient</span>()
1826 <span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span>.<span class="py-src-variable">protocol</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>
1827 <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>.<span class="py-src-variable">callLater</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">clock</span>.<span class="py-src-variable">callLater</span>
1828 <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>.<span class="py-src-variable">makeConnection</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span>)
1831 <span class="py-src-keyword">def</span> <span class="py-src-identifier">_test</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">operation</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>, <span class="py-src-parameter">expected</span>):
1832 <span class="py-src-variable">d</span> = <span class="py-src-variable">getattr</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>, <span class="py-src-variable">operation</span>)(<span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
1833 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span>.<span class="py-src-variable">value</span>(), <span class="py-src-string">'%s %d %d\r\n'</span> % (<span class="py-src-variable">operation</span>, <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>))
1834 <span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span>.<span class="py-src-variable">clear</span>()
1835 <span class="py-src-variable">d</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>, <span class="py-src-variable">expected</span>)
1836 <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>.<span class="py-src-variable">dataReceived</span>(<span class="py-src-string">"%d\r\n"</span> % (<span class="py-src-variable">expected</span>,))
1837 <span class="py-src-keyword">return</span> <span class="py-src-variable">d</span>
1840 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_add</span>(<span class="py-src-parameter">self</span>):
1841 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">'add'</span>, <span class="py-src-number">7</span>, <span class="py-src-number">6</span>, <span class="py-src-number">13</span>)
1844 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_subtract</span>(<span class="py-src-parameter">self</span>):
1845 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">'subtract'</span>, <span class="py-src-number">82</span>, <span class="py-src-number">78</span>, <span class="py-src-number">4</span>)
1848 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_multiply</span>(<span class="py-src-parameter">self</span>):
1849 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">'multiply'</span>, <span class="py-src-number">2</span>, <span class="py-src-number">8</span>, <span class="py-src-number">16</span>)
1852 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_divide</span>(<span class="py-src-parameter">self</span>):
1853 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_test</span>(<span class="py-src-string">'divide'</span>, <span class="py-src-number">14</span>, <span class="py-src-number">3</span>, <span class="py-src-number">4</span>)
1856 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_timeout</span>(<span class="py-src-parameter">self</span>):
1857 <span class="py-src-variable">d</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>.<span class="py-src-variable">add</span>(<span class="py-src-number">9</span>, <span class="py-src-number">4</span>)
1858 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span>.<span class="py-src-variable">value</span>(), <span class="py-src-string">'add 9 4\r\n'</span>)
1859 <span class="py-src-variable">self</span>.<span class="py-src-variable">clock</span>.<span class="py-src-variable">advance</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>.<span class="py-src-variable">timeOut</span>)
1860 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">assertFailure</span>(<span class="py-src-variable">d</span>, <span class="py-src-variable">ClientTimeoutError</span>)
1863 <span class="py-src-keyword">def</span> <span class="py-src-identifier">test_timeoutConnectionLost</span>(<span class="py-src-parameter">self</span>):
1864 <span class="py-src-variable">called</span> = []
1865 <span class="py-src-keyword">def</span> <span class="py-src-identifier">lost</span>(<span class="py-src-parameter">arg</span>):
1866 <span class="py-src-variable">called</span>.<span class="py-src-variable">append</span>(<span class="py-src-variable">True</span>)
1867 <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>.<span class="py-src-variable">connectionLost</span> = <span class="py-src-variable">lost</span>
1869 <span class="py-src-variable">d</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>.<span class="py-src-variable">add</span>(<span class="py-src-number">9</span>, <span class="py-src-number">4</span>)
1870 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">tr</span>.<span class="py-src-variable">value</span>(), <span class="py-src-string">'add 9 4\r\n'</span>)
1871 <span class="py-src-variable">self</span>.<span class="py-src-variable">clock</span>.<span class="py-src-variable">advance</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">proto</span>.<span class="py-src-variable">timeOut</span>)
1873 <span class="py-src-keyword">def</span> <span class="py-src-identifier">check</span>(<span class="py-src-parameter">ignore</span>):
1874 <span class="py-src-variable">self</span>.<span class="py-src-variable">assertEqual</span>(<span class="py-src-variable">called</span>, [<span class="py-src-variable">True</span>])
1875 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">assertFailure</span>(<span class="py-src-variable">d</span>, <span class="py-src-variable">ClientTimeoutError</span>).<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">check</span>)
1876 </pre><div class="caption">Source listing - <a href="listings/trial/calculus/test/test_client_3.py"><span class="filename">listings/trial/calculus/test/test_client_3.py</span></a></div></div>
1877 <p>What have we done here ?
1879 <li>We switched to StringTransportWithDisconnection. This transport manages
1880 <code class="python">loseConnection</code> and forwards it to its protocol.</li>
1881 <li>We assign the protocol to the transport via the <code class="python">protocol
1882 </code> attribute.</li>
1883 <li>We check that after a timeout our connection has closed.</li>
1887 <p>For doing that, we then use the <code class="python">TimeoutMixin</code>
1888 class, that does almost everything we want. The great thing is that it almost
1889 changes nothing to our class.</p>
1891 <div class="py-listing"><pre><p class="py-linenumber"> 1
1944 </p><span class="py-src-comment"># -*- test-case-name: calculus.test.test_client -*-</span>
1946 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">protocols</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">basic</span>, <span class="py-src-variable">policies</span>
1947 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">defer</span>
1951 <span class="py-src-keyword">class</span> <span class="py-src-identifier">ClientTimeoutError</span>(<span class="py-src-parameter">Exception</span>):
1952 <span class="py-src-keyword">pass</span>
1956 <span class="py-src-keyword">class</span> <span class="py-src-identifier">RemoteCalculationClient</span>(<span class="py-src-parameter">object</span>, <span class="py-src-parameter">basic</span>.<span class="py-src-parameter">LineReceiver</span>, <span class="py-src-parameter">policies</span>.<span class="py-src-parameter">TimeoutMixin</span>):
1958 <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>):
1959 <span class="py-src-variable">self</span>.<span class="py-src-variable">results</span> = []
1960 <span class="py-src-variable">self</span>.<span class="py-src-variable">_timeOut</span> = <span class="py-src-number">60</span>
1962 <span class="py-src-keyword">def</span> <span class="py-src-identifier">lineReceived</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">line</span>):
1963 <span class="py-src-variable">self</span>.<span class="py-src-variable">setTimeout</span>(<span class="py-src-variable">None</span>)
1964 <span class="py-src-variable">d</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">results</span>.<span class="py-src-variable">pop</span>(<span class="py-src-number">0</span>)
1965 <span class="py-src-variable">d</span>.<span class="py-src-variable">callback</span>(<span class="py-src-variable">int</span>(<span class="py-src-variable">line</span>))
1968 <span class="py-src-keyword">def</span> <span class="py-src-identifier">timeoutConnection</span>(<span class="py-src-parameter">self</span>):
1969 <span class="py-src-keyword">for</span> <span class="py-src-variable">d</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">results</span>:
1970 <span class="py-src-variable">d</span>.<span class="py-src-variable">errback</span>(<span class="py-src-variable">ClientTimeoutError</span>())
1971 <span class="py-src-variable">self</span>.<span class="py-src-variable">transport</span>.<span class="py-src-variable">loseConnection</span>()
1974 <span class="py-src-keyword">def</span> <span class="py-src-identifier">_sendOperation</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">op</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
1975 <span class="py-src-variable">d</span> = <span class="py-src-variable">defer</span>.<span class="py-src-variable">Deferred</span>()
1976 <span class="py-src-variable">self</span>.<span class="py-src-variable">results</span>.<span class="py-src-variable">append</span>(<span class="py-src-variable">d</span>)
1977 <span class="py-src-variable">line</span> = <span class="py-src-string">"%s %d %d"</span> % (<span class="py-src-variable">op</span>, <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
1978 <span class="py-src-variable">self</span>.<span class="py-src-variable">sendLine</span>(<span class="py-src-variable">line</span>)
1979 <span class="py-src-variable">self</span>.<span class="py-src-variable">setTimeout</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">_timeOut</span>)
1980 <span class="py-src-keyword">return</span> <span class="py-src-variable">d</span>
1983 <span class="py-src-keyword">def</span> <span class="py-src-identifier">add</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
1984 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_sendOperation</span>(<span class="py-src-string">"add"</span>, <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
1987 <span class="py-src-keyword">def</span> <span class="py-src-identifier">subtract</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
1988 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_sendOperation</span>(<span class="py-src-string">"subtract"</span>, <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
1991 <span class="py-src-keyword">def</span> <span class="py-src-identifier">multiply</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
1992 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_sendOperation</span>(<span class="py-src-string">"multiply"</span>, <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
1995 <span class="py-src-keyword">def</span> <span class="py-src-identifier">divide</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">a</span>, <span class="py-src-parameter">b</span>):
1996 <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_sendOperation</span>(<span class="py-src-string">"divide"</span>, <span class="py-src-variable">a</span>, <span class="py-src-variable">b</span>)
1997 </pre><div class="caption">Source listing - <a href="listings/trial/calculus/client_3.py"><span class="filename">listings/trial/calculus/client_3.py</span></a></div></div>
1999 <h2>Code coverage<a name="auto13"/></h2>
2001 <p>Code coverage is one of the aspects of software testing that shows how much
2002 your tests cross (cover) the code of your program. There are different kind of
2003 measures: path coverage, condition coverage, statement coverage... We'll only
2004 consider statement coverage here, whether a line has been executed or not.
2007 <p>Trial has an option to generate the statement coverage of your tests.
2008 This option is --coverage. It creates a coverage directory in _trial_temp,
2009 with a file .cover for every modules used during the tests. The ones
2010 interesting for us are calculus.base.cover and calculus.remote.cover. In
2011 front of each line is the number of times you went through during the
2012 tests, or the marker '>>>>>>' if the line was not
2013 covered. If you went through all the tutorial to this point, you should
2014 have complete coverage :).</p>
2016 <p>Again, this is only another useful pointer, but it doesn't mean your
2017 code is perfect: your tests should consider every possibile input and
2018 output, to get <strong>full</strong> coverage (condition, path, etc.) as well
2021 <h2>Conclusion<a name="auto14"/></h2>
2023 <p>So what did you learn in this document?
2025 <li>How to use the trial command-line tool to run your tests</li>
2026 <li>How to use string transports to test individual clients and servers
2027 without creating sockets</li>
2028 <li>If you really want to create sockets, how to cleanly do it so that it
2029 doesn't have bad side effects</li>
2030 <li>And some small tips you can't live without.</li>
2032 If one of the topics still looks cloudy to you, please give us your feedback!
2033 You can file tickets to improve this document
2034 <a href="http://twistedmatrix.com/" shape="rect">on the Twisted web site</a>.
2039 <p><a href="index.html">Index</a></p>
2040 <span class="version">Version: 12.1.0</span>