[Twisted-Python] Testing Strategies (was Re: Streaming Requests)

Glyph Lefkowitz glyph at twistedmatrix.com
Thu Jan 12 22:56:46 MST 2017


> On Jan 12, 2017, at 4:24 AM, Jean-Paul Calderone <exarkun at twistedmatrix.com> wrote:
> 
> On Wed, Jan 11, 2017 at 11:41 PM, Glyph Lefkowitz <glyph at twistedmatrix.com <mailto:glyph at twistedmatrix.com>> wrote:
> 
>> On Jan 9, 2017, at 4:13 AM, Jean-Paul Calderone <exarkun at twistedmatrix.com <mailto:exarkun at twistedmatrix.com>> wrote:
>> 
>> On Mon, Jan 9, 2017 at 4:52 AM, Glyph Lefkowitz <glyph at twistedmatrix.com <mailto:glyph at twistedmatrix.com>> wrote:
>> On a related note, 'proto_helpers' is in a super awkward place.  'twisted.testing', anyone? :)
>> 
>> Yes.  I almost suggested this about a week ago when I was preparing to contribute some testing code but then realized I couldn't contribute the code after all. :(
> 
> Well, now it's just a matter of time :).
> 
> Some further thoughts on this... What I've actually been doing elsewhere (mostly) is to put the testing implementations quite near the real implementations.  For example, I recently introduced `workproject.subscription_manager.{network_client,memory_client}`.  But contrary to this, in txAWS I'm following the pre-existing pattern which is more like `txaws.service.AWSServiceRegion.get_ec2_client` & `txaws.testing.service.FakeAWSServiceRegion.get_ec2_client`.

"Memory" implementations are definitely a step up the value hierarchy from "fake" implementations.

I guess maybe both of these patterns make sense for certain things.  It makes sense to put a memory implementation next to the real one (c.f. SQLite), but it doesn't make as much sense to put a testing implementation there.  I would make this distinction by saying a "testing" implementation is one which has assertion method helpers, simulated I/O triggers, and similar.

> Which I prefer, I'm not really sure yet.  Role-named modules are easy to recognize.  On the other hand, testing code next to implementation code is easier to stumble across and you don't have to recognize a separate testing module to find it.  Also, role-named modules tend to pile up and get in each other's way (start working with `twisted.testing`, `txaws.testing`, etc and things start to get confusing quickly).

Yeah, and I guess it'll actually be `twisted.internet.testing`, `twisted.python.testing`, `twisted.web.testing` etc.  Which we explicitly rejected with `interfaces` because it got too confusing; the existence of multiple `endpoints` modules has been mildly irritating while working on HostnameEndpoint for the last couple of days.

> Additionally, a related idea is that often the testing implementation might actually serve as a good starting place for the real implementation.  I think the code in txAWS is a good example of this (ie, the real and fake implementations duplicate a lot of logic with some pointless divergences).  In the olden days I would have said "the testing implementation is often a good base class for the real implementation".  But now I'll mumble something about composition.

Yeah, I think this is the broader point, actually - the best possible "fake" implementation is a completely real implementation where the lower level it's composing against is fake.

> Put another way:
> Clock should be the real implementation of all of the time-source independent logic of IReactorTime
One minor tweak: Clock itself should be split into two objects, one of which has just 'advance' and one of which has IReactorTime <https://glyph.twistedmatrix.com/2015/05/separate-your-fakes-and-your-inspectors.html <https://glyph.twistedmatrix.com/2015/05/separate-your-fakes-and-your-inspectors.html>>.  Possibly three objects, since it gets its `seconds` from somewhere else?
> FakeAWSServiceRegion should be the real implementation of the "client factory" logic (get_XXX_client) of AWSServiceRegion 
> and so on.

Yes, definitely.

> Perhaps this also speaks to the idea you mentioned that there should be a stack of thin abstractions but I snipped that content already so I'll try responding to that separately, later.


Ideally, the only "fake" implementation we should need is StringTransport and Clock; the abstractions in the middle would be wiring together the layers in a way that's convenient for test clients to access.

-glyph

-------------- next part --------------
An HTML attachment was scrubbed...
URL: </pipermail/twisted-python/attachments/20170112/2b17487a/attachment-0002.html>


More information about the Twisted-Python mailing list