introduce and encourage use of an interface that is more easily testable
See original GitHub issueI’ve had some difficulty writing good unit tests for WebSocketClientProtocol-based implementations. The tools available seem to encourage one of two paths:
- Directly instantiate the relevant objects and then call methods on them as though they were in use by the real Autobahn WebSocket implementation.
- Set up real network servers and clients that interact over real sockets so that the real Autobahn WebSocket implementation is driving them.
The first of these options has the drawback that there’s no guarantee the tests will actually drive the application code correctly (and even if it is initially correct the chances of it diverging from reality increase as Autobahn is maintained and changed).
The second of these options has the drawback that it is prone to many different kinds of spurious failure (most code that takes this approach doesn’t even manage to get the part where you start listening on a port correct 😢 ). It also draws in a lot more code and so failures can become harder to spot. Failures can also end up being caused by other code that’s not the main focus of the test. It’s also a lot slower/more expensive as a result of running so much more code.
I think it would be very useful if Autobahn itself offered and encouraged the use of a higher-level interface that allows details about connections/sockets/networks to be contained in an object that can easily be substituted for a testing fake. A model for this which appears to be working well is the regular HTTP client interface in Twisted, twisted.web.client.IAgent
. This has one primary network-enabled implementation in Twisted, twisted.web.client.Agent
and the third-party(-ish) treq library provides a test-focused implementation, treq.testing.RequestTraversalAgent
.
Application code written against IAgent
can accept an implementation and do what it needs to do. Real-world use of this application code passes in an Agent
instance (or possibly a wrapper but that’s not directly relevant here). Test use of this same application code passes in a correctly initialized RequestTraversalAgent
. The application code doesn’t know or care which implementation it gets. Agent
goes off and makes real requests over a real network. RequestTraversalAgent
decomposes the request and serves up a response to it using a twisted.web.resource.IResource
provider - with no network, only Python method calls and correct copying of bytes from one place to another in memory.
Issue Analytics
- State:
- Created 4 years ago
- Comments:23 (22 by maintainers)
Top GitHub Comments
So, I think I’m proposing:
IWebSocketClientAgent.open()
takes something that contains all the options (or, maybe, has kwargs that are the options?). This object would be very much like howFactory
is already (ab)used, right?Was this closed by #1186 or are there residual improvements to be implemented?