SHOW:
|
|
- or go back to the newest paste.
| 1 | from twisted.trial.unittest import TestCase | |
| 2 | from twisted.protocols.amp import ( | |
| 3 | Command, Integer, CommandLocator, BoxDispatcher, COMMAND, ASK, ANSWER) | |
| 4 | ||
| 5 | ################### Application code ################### | |
| 6 | ||
| 7 | ||
| 8 | class Sum(Command): | |
| 9 | ||
| 10 | arguments = [("a", Integer()), ("b", Integer())]
| |
| 11 | response = [("result", Integer())]
| |
| 12 | ||
| 13 | ||
| 14 | class Math(CommandLocator): | |
| 15 | ||
| 16 | @Sum.responder | |
| 17 | def sum(self, a, b): | |
| 18 | return {"result": a + b}
| |
| 19 | ||
| 20 | ||
| 21 | class MathClient(object): | |
| 22 | """This is an example of application client code using C{callRemote}.
| |
| 23 | ||
| 24 | It just provides a convenient abstraction around the application-defined | |
| 25 | AMP commands. | |
| 26 | """ | |
| 27 | ||
| 28 | def __init__(self, protocol): | |
| 29 | """ | |
| 30 | @param protocol: An C{AMP} instance connected to a remote L{Math}
| |
| 31 | command locator. | |
| 32 | """ | |
| 33 | self._protocol = protocol | |
| 34 | ||
| 35 | def sum(self, a, b): | |
| 36 | deferred = self._protocol.callRemote(Sum, a=a, b=b) | |
| 37 | return deferred.addCallback(lambda response: response["result"]) | |
| 38 | ||
| 39 | ||
| 40 | ################### Application tests ################### | |
| 41 | ||
| 42 | ||
| 43 | class FakeBoxSender(object): | |
| 44 | ||
| 45 | def __init__(self): | |
| 46 | self.sent = [] | |
| 47 | ||
| 48 | def sendBox(self, box): | |
| 49 | self.sent.append(box) | |
| 50 | ||
| 51 | ||
| 52 | class MathTest(TestCase): | |
| 53 | ||
| 54 | def setUp(self): | |
| 55 | super(MathTest, self).setUp() | |
| 56 | - | self.locator = Math() |
| 56 | + | |
| 57 | - | self.dispatcher = BoxDispatcher(self.locator) |
| 57 | + | self.dispatcher = BoxDispatcher(locator) |
| 58 | self.sender = FakeBoxSender() | |
| 59 | self.dispatcher.startReceivingBoxes(self.sender) | |
| 60 | ||
| 61 | def test_sum_request(self): | |
| 62 | """ | |
| 63 | This is mainly a wiring test, asserting that L{Math.sum} is decorated
| |
| 64 | with @Sum.responder. | |
| 65 | ||
| 66 | Incidentally it also exercises L{Sum.arguments} and implies that the
| |
| 67 | 'a' and 'b' arguments are deserialized to C{int}s.
| |
| 68 | ||
| 69 | One would ideally also test the L{Method.sum} on its own directly, by
| |
| 70 | instantiating the L{Math} class, and really exercising all its actual
| |
| 71 | logic. | |
| 72 | """ | |
| 73 | box = {COMMAND: "Sum", ASK: "1", "a": "3", "b": "2"}
| |
| 74 | self.dispatcher.ampBoxReceived(box) | |
| 75 | self.assertEqual(self.sender.sent, [{ANSWER: "1", "result": "5"}])
| |
| 76 | ||
| 77 | def test_sum_response(self): | |
| 78 | """ | |
| 79 | Maybe there should be a test for exercising L{Sum.response} too?
| |
| 80 | ||
| 81 | It would directly or indirectly, make sure that the 'result' field is | |
| 82 | deserialized to C{int}.
| |
| 83 | """ | |
| 84 | ||
| 85 | ||
| 86 | class FakeBoxDispatcher(object): | |
| 87 | ||
| 88 | def __init__(self, locator): | |
| 89 | self.locator = locator | |
| 90 | ||
| 91 | def callRemote(self, commandType, *a, **kw): | |
| 92 | """ | |
| 93 | Faking C{callRemote} this way is slightly unfortunate because it
| |
| 94 | duplicates the real AMP logic a bit, but it might acceptable. | |
| 95 | """ | |
| 96 | responder = self.locator.locateResponder(commandType.commandName) | |
| 97 | deferred = responder(kw) | |
| 98 | return deferred.addCallback(commandType.parseResponse, self) | |
| 99 | ||
| 100 | ||
| 101 | class MathClientTest(TestCase): | |
| 102 | ||
| 103 | def setUp(self): | |
| 104 | super(MathClientTest, self).setUp() | |
| 105 | locator = Math() | |
| 106 | dispatcher = FakeBoxDispatcher(locator) | |
| 107 | self.client = MathClient(dispatcher) | |
| 108 | ||
| 109 | def test_sum(self): | |
| 110 | """ | |
| 111 | This tests L{MathClient}, and it does so by running a real L{Math}
| |
| 112 | command locator, except that there is no network/transport involved. | |
| 113 | ||
| 114 | Other layers of the application code using L{MathClient} could as well
| |
| 115 | have their tests setup using a real L{MatchClient} instantiated with
| |
| 116 | a L{FakeBoxDispatcher} as well, as opposed to creating fake versions
| |
| 117 | of L{MathClient}.
| |
| 118 | ||
| 119 | This would of course make such higher-layer tests more "integration" | |
| 120 | and less "unit", but in practice it might be more convenient and | |
| 121 | give more confidence that mocking out L{MathClient} (drifting to
| |
| 122 | state-based vs. behavior-based testing here, sorry). | |
| 123 | """ | |
| 124 | deferred = self.client.sum(3, 2) | |
| 125 | self.assertEqual(5, self.successResultOf(deferred)) |