0
# Testing Framework
1
2
Comprehensive unit testing framework with asynchronous test support and specialized test runners. Twisted Trial extends Python's unittest with Deferred support, making it ideal for testing asynchronous code.
3
4
## Capabilities
5
6
### Test Cases
7
8
Base test case classes with support for asynchronous operations and Twisted-specific testing patterns.
9
10
```python { .api }
11
class trial.unittest.TestCase:
12
"""
13
Asynchronous-capable test case for testing Twisted code.
14
Extends unittest.TestCase with Deferred support.
15
16
Attributes:
17
- timeout: Default test timeout in seconds
18
"""
19
timeout = 120.0
20
21
def setUp(self):
22
"""
23
Set up test fixture. Can return Deferred for async setup.
24
25
Returns:
26
None or Deferred: Completion indicator
27
"""
28
29
def tearDown(self):
30
"""
31
Clean up test fixture. Can return Deferred for async cleanup.
32
33
Returns:
34
None or Deferred: Completion indicator
35
"""
36
37
def addCleanup(self, function, *args, **kwargs):
38
"""
39
Add cleanup function to run after test.
40
41
Args:
42
function: Cleanup function
43
*args, **kwargs: Arguments for function
44
"""
45
46
def doCleanups(self):
47
"""
48
Run all registered cleanup functions.
49
50
Returns:
51
None or Deferred: Completion indicator
52
"""
53
54
def assertFailure(self, deferred, *expectedFailures):
55
"""
56
Assert that a Deferred will fail with specific exception types.
57
58
Args:
59
deferred (Deferred): Deferred to test
60
*expectedFailures: Expected exception types
61
62
Returns:
63
Deferred[Failure]: Fires with failure when deferred fails
64
"""
65
66
def successResultOf(self, deferred):
67
"""
68
Get successful result of already-fired Deferred.
69
70
Args:
71
deferred (Deferred): Deferred that should have succeeded
72
73
Returns:
74
Result value of the Deferred
75
"""
76
77
def failureResultOf(self, deferred, *expectedFailures):
78
"""
79
Get failure result of already-failed Deferred.
80
81
Args:
82
deferred (Deferred): Deferred that should have failed
83
*expectedFailures: Expected exception types
84
85
Returns:
86
Failure: The failure
87
"""
88
89
def assertNoResult(self, deferred):
90
"""
91
Assert that Deferred has not yet fired.
92
93
Args:
94
deferred (Deferred): Deferred to check
95
"""
96
97
class trial.unittest.SynchronousTestCase:
98
"""
99
Synchronous test case that cannot handle Deferreds.
100
Use for tests that don't involve any asynchronous operations.
101
Faster than TestCase but cannot return Deferreds from test methods.
102
"""
103
def setUp(self):
104
"""Set up test fixture synchronously."""
105
106
def tearDown(self):
107
"""Clean up test fixture synchronously."""
108
109
# Test decorators and markers
110
class trial.unittest.Todo:
111
"""
112
Mark a test as expected to fail (todo item).
113
"""
114
def __init__(self, reason, errors=None):
115
"""
116
Args:
117
reason (str): Reason for expected failure
118
errors: Expected error types
119
"""
120
121
def trial.unittest.skip(reason):
122
"""
123
Skip a test with given reason.
124
125
Args:
126
reason (str): Skip reason
127
128
Returns:
129
Decorator function
130
"""
131
132
def trial.unittest.skipIf(condition, reason):
133
"""
134
Skip test if condition is true.
135
136
Args:
137
condition (bool): Skip condition
138
reason (str): Skip reason
139
140
Returns:
141
Decorator function
142
"""
143
144
class trial.unittest.SkipTest(Exception):
145
"""
146
Exception raised to skip a test.
147
"""
148
149
class trial.unittest.FailTest(Exception):
150
"""
151
Exception raised to fail a test.
152
"""
153
```
154
155
**Test Case Usage Example**:
156
157
```python
158
from twisted.trial import unittest
159
from twisted.internet import defer, reactor, task
160
161
class AsyncTestCase(unittest.TestCase):
162
163
def setUp(self):
164
# Async setup
165
self.service = MyService()
166
return self.service.start()
167
168
def tearDown(self):
169
# Async cleanup
170
return self.service.stop()
171
172
def test_async_operation(self):
173
"""Test that returns a Deferred."""
174
d = self.service.processData("test")
175
d.addCallback(self.assertEqual, "expected_result")
176
return d
177
178
def test_failure_case(self):
179
"""Test expected failure."""
180
d = self.service.badOperation()
181
return self.assertFailure(d, ValueError, TypeError)
182
183
@defer.inlineCallbacks
184
def test_inline_callbacks(self):
185
"""Test using inlineCallbacks for cleaner async code."""
186
result1 = yield self.service.step1()
187
result2 = yield self.service.step2(result1)
188
self.assertEqual(result2, "expected")
189
190
def test_synchronous_result(self):
191
"""Test already-completed Deferred."""
192
d = defer.succeed("immediate")
193
result = self.successResultOf(d)
194
self.assertEqual(result, "immediate")
195
196
class SyncTestCase(unittest.SynchronousTestCase):
197
198
def test_pure_sync(self):
199
"""Test that doesn't involve any async operations."""
200
result = self.service.sync_method()
201
self.assertEqual(result, "expected")
202
```
203
204
### Test Suites and Discovery
205
206
Test suite management and test discovery mechanisms.
207
208
```python { .api }
209
class trial.unittest.TestSuite:
210
"""
211
Container for multiple test cases.
212
"""
213
def __init__(self, tests=()):
214
"""
215
Args:
216
tests: Sequence of test cases
217
"""
218
219
def addTest(self, test):
220
"""
221
Add a test to the suite.
222
223
Args:
224
test: Test case or suite to add
225
"""
226
227
def addTests(self, tests):
228
"""
229
Add multiple tests to the suite.
230
231
Args:
232
tests: Sequence of tests to add
233
"""
234
235
def countTestCases(self):
236
"""
237
Count total number of test cases.
238
239
Returns:
240
int: Number of test cases
241
"""
242
243
class runner.TestLoader:
244
"""
245
Test discovery and loading.
246
"""
247
def loadTestsFromTestCase(self, testCaseClass):
248
"""
249
Load tests from a test case class.
250
251
Args:
252
testCaseClass: Test case class
253
254
Returns:
255
TestSuite: Suite containing tests
256
"""
257
258
def loadTestsFromModule(self, module):
259
"""
260
Load tests from a module.
261
262
Args:
263
module: Python module containing tests
264
265
Returns:
266
TestSuite: Suite containing tests
267
"""
268
269
def loadTestsFromName(self, name):
270
"""
271
Load tests from dotted name.
272
273
Args:
274
name (str): Dotted test name
275
276
Returns:
277
TestSuite: Suite containing tests
278
"""
279
280
def unittest.decorate(testClass, classDecorator):
281
"""
282
Apply decorator to all test methods in a class.
283
284
Args:
285
testClass: Test case class
286
classDecorator: Decorator function
287
288
Returns:
289
Decorated test class
290
"""
291
```
292
293
### Test Runners
294
295
Test execution engines with various output formats and reporting options.
296
297
```python { .api }
298
class runner.TrialRunner:
299
"""
300
Main test runner for executing trial tests.
301
302
Attributes:
303
- stream: Output stream for results
304
- tbformat: Traceback format ('default', 'brief', 'verbose')
305
- args: Command line arguments
306
"""
307
stream = None
308
tbformat = 'default'
309
310
def __init__(self, reporterFactory, mode=None, logfile='-', stream=None, profile=False, tracebackFormat='default', realTimeErrors=False, uncleanWarnings=False, workingDirectory=None, tests=None):
311
"""
312
Args:
313
reporterFactory: Factory for creating result reporters
314
mode: Test mode ('debug', etc.)
315
logfile: Path for log output
316
stream: Output stream
317
profile: Whether to enable profiling
318
tracebackFormat: Format for tracebacks
319
realTimeErrors: Show errors in real time
320
uncleanWarnings: Show cleanup warnings
321
workingDirectory: Working directory for tests
322
tests: Test suite to run
323
"""
324
325
def run(self, test):
326
"""
327
Run tests with this runner.
328
329
Args:
330
test: Test case or suite to run
331
332
Returns:
333
TestResult: Test execution results
334
"""
335
336
class runner.LoggedSuite:
337
"""
338
Test suite that logs test execution.
339
"""
340
def __init__(self, tests=(), logfile=None):
341
"""
342
Args:
343
tests: Test cases to include
344
logfile: File for logging output
345
"""
346
```
347
348
### Test Reporters
349
350
Result reporting and output formatting for test runs.
351
352
```python { .api }
353
class reporter.TestResult:
354
"""
355
Test result collector and reporter base class.
356
357
Attributes:
358
- successes: Number of successful tests
359
- errors: List of test errors
360
- failures: List of test failures
361
- skips: List of skipped tests
362
- expectedFailures: List of expected failures
363
- unexpectedSuccesses: List of unexpected successes
364
"""
365
successes = 0
366
errors = None
367
failures = None
368
skips = None
369
expectedFailures = None
370
unexpectedSuccesses = None
371
372
def startTest(self, test):
373
"""
374
Called when test starts.
375
376
Args:
377
test: Test case being started
378
"""
379
380
def stopTest(self, test):
381
"""
382
Called when test ends.
383
384
Args:
385
test: Test case that ended
386
"""
387
388
def addError(self, test, error):
389
"""
390
Add test error.
391
392
Args:
393
test: Test case
394
error: Error tuple (type, value, traceback)
395
"""
396
397
def addFailure(self, test, failure):
398
"""
399
Add test failure.
400
401
Args:
402
test: Test case
403
failure: Failure tuple (type, value, traceback)
404
"""
405
406
def addSuccess(self, test):
407
"""
408
Add successful test.
409
410
Args:
411
test: Test case
412
"""
413
414
def addSkip(self, test, reason):
415
"""
416
Add skipped test.
417
418
Args:
419
test: Test case
420
reason (str): Skip reason
421
"""
422
423
class reporter.TreeReporter:
424
"""
425
Tree-style test reporter showing hierarchical test structure.
426
"""
427
def __init__(self, stream, tbformat='default', realtime=False, publisher=None):
428
"""
429
Args:
430
stream: Output stream
431
tbformat (str): Traceback format
432
realtime (bool): Show results in real time
433
publisher: Log publisher
434
"""
435
436
class reporter.VerboseTextReporter:
437
"""
438
Verbose text reporter with detailed output.
439
"""
440
def __init__(self, stream, tbformat='default', realtime=False, publisher=None):
441
"""
442
Args:
443
stream: Output stream
444
tbformat (str): Traceback format
445
realtime (bool): Show results in real time
446
publisher: Log publisher
447
"""
448
449
class reporter.MinimalReporter:
450
"""
451
Minimal test reporter with concise output.
452
"""
453
def __init__(self, stream, tbformat='default', realtime=False, publisher=None):
454
"""
455
Args:
456
stream: Output stream
457
tbformat (str): Traceback format
458
realtime (bool): Show results in real time
459
publisher: Log publisher
460
"""
461
462
class reporter.TimingTextReporter:
463
"""
464
Text reporter that includes timing information.
465
"""
466
def __init__(self, stream, tbformat='default', realtime=False, publisher=None):
467
"""
468
Args:
469
stream: Output stream
470
tbformat (str): Traceback format
471
realtime (bool): Show results in real time
472
publisher: Log publisher
473
"""
474
475
class reporter.SubunitReporter:
476
"""
477
Reporter that outputs results in Subunit format.
478
"""
479
def __init__(self, stream, tbformat='default', realtime=False, publisher=None):
480
"""
481
Args:
482
stream: Output stream
483
tbformat (str): Traceback format
484
realtime (bool): Show results in real time
485
publisher: Log publisher
486
"""
487
```
488
489
### Testing Utilities
490
491
Helper functions and utilities for writing and running tests.
492
493
```python { .api }
494
class util.TestCase:
495
"""Additional testing utilities."""
496
497
def patch(self, obj, attribute, value):
498
"""
499
Patch an object attribute for the duration of the test.
500
501
Args:
502
obj: Object to patch
503
attribute (str): Attribute name
504
value: New attribute value
505
"""
506
507
def mktemp(self):
508
"""
509
Create a temporary file path unique to this test.
510
511
Returns:
512
str: Temporary file path
513
"""
514
515
def util.suppress(action=None, *suppressedWarnings):
516
"""
517
Context manager/decorator to suppress warnings during tests.
518
519
Args:
520
action: Warning action ('ignore', 'error', etc.)
521
*suppressedWarnings: Warning categories to suppress
522
"""
523
524
def util.acquireAttribute(objects, attr, default=None):
525
"""
526
Get attribute from first object that has it.
527
528
Args:
529
objects: Sequence of objects to check
530
attr (str): Attribute name
531
default: Default value if not found
532
533
Returns:
534
Attribute value or default
535
"""
536
537
# Test constants
538
util.DEFAULT_TIMEOUT_DURATION = 120.0 # Default test timeout
539
```
540
541
**Testing Utilities Example**:
542
543
```python
544
from twisted.trial import unittest, util
545
546
class UtilityTestCase(unittest.TestCase):
547
548
def setUp(self):
549
self.temp_file = self.mktemp() # Unique temp file path
550
self.original_value = MyClass.class_attribute
551
552
def test_with_patch(self):
553
# Temporarily patch an attribute
554
self.patch(MyClass, 'class_attribute', 'test_value')
555
self.assertEqual(MyClass.class_attribute, 'test_value')
556
# Attribute restored automatically after test
557
558
@util.suppress('ignore', DeprecationWarning)
559
def test_with_suppressed_warnings(self):
560
# This test won't show deprecation warnings
561
deprecated_function()
562
563
def tearDown(self):
564
# Clean up temp file if created
565
if os.path.exists(self.temp_file):
566
os.unlink(self.temp_file)
567
```
568
569
### Mock Objects and Test Doubles
570
571
Utilities for creating mock objects and test doubles in Twisted tests.
572
573
```python { .api }
574
class proto_helpers.StringTransport:
575
"""
576
Transport that stores written data in memory for testing.
577
578
Attributes:
579
- value: Data written to transport (bytes)
580
- disconnected: Whether transport is disconnected
581
"""
582
value = b''
583
disconnected = False
584
585
def write(self, data):
586
"""
587
Write data to in-memory buffer.
588
589
Args:
590
data (bytes): Data to write
591
"""
592
593
def writeSequence(self, data):
594
"""
595
Write sequence of data chunks.
596
597
Args:
598
data: Sequence of bytes objects
599
"""
600
601
def loseConnection(self):
602
"""Mark transport as disconnected."""
603
604
def clear(self):
605
"""Clear stored data."""
606
607
def value(self):
608
"""
609
Get all written data.
610
611
Returns:
612
bytes: All data written to transport
613
"""
614
615
class proto_helpers.StringTransportWithDisconnection:
616
"""
617
StringTransport that notifies protocol of disconnection.
618
"""
619
620
def loseConnection(self):
621
"""Disconnect and notify protocol."""
622
623
class proto_helpers.AccumulatingProtocol:
624
"""
625
Protocol that accumulates all received data.
626
627
Attributes:
628
- data: All received data (bytes)
629
- transport: Connected transport
630
"""
631
data = b''
632
transport = None
633
634
def dataReceived(self, data):
635
"""
636
Accumulate received data.
637
638
Args:
639
data (bytes): Received data
640
"""
641
```
642
643
**Protocol Testing Example**:
644
645
```python
646
from twisted.trial import unittest
647
from twisted.test import proto_helpers
648
649
class ProtocolTestCase(unittest.TestCase):
650
651
def setUp(self):
652
self.protocol = MyProtocol()
653
self.transport = proto_helpers.StringTransport()
654
self.protocol.makeConnection(self.transport)
655
656
def test_data_handling(self):
657
# Send data to protocol
658
self.protocol.dataReceived(b"test data")
659
660
# Check what protocol wrote back
661
sent_data = self.transport.value()
662
self.assertEqual(sent_data, b"expected response")
663
664
def test_disconnection(self):
665
# Test protocol disconnection
666
self.transport.loseConnection()
667
self.assertTrue(self.transport.disconnected)
668
669
# Protocol should handle disconnection gracefully
670
self.assertFalse(self.protocol.connected)
671
```
672
673
### Required Imports
674
675
Complete import statements for testing with Twisted Trial:
676
677
```python
678
# Core testing framework
679
from twisted.trial import unittest, runner, reporter
680
from twisted.trial.unittest import TestCase, SynchronousTestCase
681
from twisted.python.failure import Failure
682
from twisted.internet import defer
683
684
# Test decorators and utilities
685
from twisted.trial.unittest import skip, skipIf, Todo, SkipTest
686
from twisted.test import proto_helpers # For StringTransport
687
688
# Common patterns
689
from twisted.trial.unittest import TestCase as TrialTestCase
690
from twisted.internet.defer import succeed, fail, inlineCallbacks, returnValue
691
692
# Running tests
693
# Command line: trial mypackage.test.test_module
694
# In code: trial.unittest.main()
695
```