On Jul 4, 2011, at 8:01 AM, Jacob Kaplan-Moss wrote:

> On Mon, Jul 4, 2011 at 8:54 AM, Jim Dalton <jim.dal...@gmail.com> wrote:
>> I've created a ticket for this and uploaded a patch: 
>> https://code.djangoproject.com/ticket/16401
>> Please feel free to review and let me know if this is a good idea in the 
>> first place, as well as if the patch makes sense and is the right approach.
> 
> Hm.
> 
> While I think the feature's a good idea (I've been stung by tests
> running against a live cache) I can't say I'm particularly thrilled by
> the approach here. Monkeypatching is acceptable in situations like
> these, but there's a limit... and I think patching five methods is
> starting to reach it. Remember: as much as possible, tests should
> operate just like production. Any difference, no matter how small,
> introduces the worst kind of bugs: ones that only show up in
> production.

Yeah, I agree with you in principle there. I'm definitely an advocate of 
avoiding monkey patching like the plague in production code.

While I also agree that this code looks a bit hairy when you sort of squint 
your eyes at it, if you actually look closely at what's going on, we're not 
modifying things in any way that's tricky or complicated. Really, the only two 
things we are doing are adding a bit of code that that tracks keys and then 
swapping a few methods out. Pretty similar to what we do already with 
Template.render  -- just x5. The monkey patching stuff is not modifying any 
arguments or results, nor is it changing the observable behavior of the 
modified methods. The tests demonstrate this pretty thoroughly.

The only place I've managed to think of where this is different from production 
is in the length of the key. Because we are appending "_test_" to the start of 
KEY_PREFIX, you're going to get 6 less characters in your key than you would 
otherwise. It *is* a difference, so I don't want to downplay it, but at the 
same time it's a difference where you'll get an error in testing that you 
wouldn't get in production -- which is better than the opposite.

The only other place where someone might run into trouble is if they themselves 
were monkey patching the cache and their changes collided somehow. I understand 
that this is the kind of thing that happens in Ruby a lot, and it strikes me as 
unpleasant.

Anyhow, that's not to say that there isn't a better solution or that your 
proposal below isn't preferable (more on that in a sec), but I'd argue that my 
patch is at least a plausible solution, i.e. it works in lieu of a better 
alternative.



> I have an alternate proposal:
> 
> Instead of using the default cache defined in CACHES, use a "test
> cache." This can safely be flushed, meaning that no monkeypatching is
> needed. The added benefit here is that this works similarly to the
> database engine, so it'll be fairly easy to understand.
> 
> Now, unlike DB test we can't just prefix "test_" to the database name
> (because we haven't got one), nor can we prefix keys (because not all
> cache backends can do something like "delete test:*"). So I'd suggest
> we look for a "test" backend defined in CACHES and fall back to a
> local memory cache if not defined.
> 
> I think this simplifies things immensely, and also gives a fair bit of
> control -- if I want to test against a real cache backend, I just
> define CACHES['test'] and roll.


So, in many ways I like this idea. I agree that the implementation is going to 
be more straightforward. Here's just a quick list of concerns or possible 
drawbacks I was able to think of:

* Not sure how to handle multiple cache backends. I imagine if you're someone 
who has implemented multiple cache backends, then you are using them in 
specific ways for specific reasons. If your tests end up sharing a test 
backend, that could result in unpredictable behavior. One possible resolution 
would be to amend your proposal a bit: For each cache backend defined, we swap 
it out with the locmem backend unless you have yourself defined a 
test_CACHE_NAME version of that cache. So e.g. fi I have three backends named 
"default", "file" and "db", I then need to define test_default, test_file and 
test_db if I want to specify how the test framework is going to set up the test 
cache. Otherwise, it'll fall back to replacing it with a locmem cache.
* At least when it comes to memcached, this doesn't help you much with the 
cache.clear() issue, since cache.clear() will blank your entire memcached 
cache. So if we're calling cache.clear() at the end of each test and it's 
blanking the whole cache, I start to wonder why we bother at all? In other 
words, why not just call cache.clear() at the end of every test and call it a 
day, and make a note in the documentation that running the test will clear any 
cache you have defined in BACKENDS, so use it with caution. One response is 
that the same thing is not true for db, file and locmem -- in these cases 
you'll only be deleting the test cache which was set up for them and leaving 
everything else intact. But IDK, I mean I get the sense that memcached is what 
most people are using for caching in production, so to me it's far and away the 
most import cache to consider.

Those were my major concerns, though I'm not sure any of them are fatal at 
least from my perspective.

At present our choices boil down to:

1. Monkey patch cache, keep track of set keys, flush set keys at the end of 
each test.
2. Default to locmem cache, use a 'test' cache (or possibly 'test_CACHE_NAME') 
instead if found. cache.clear() on test after each test.
3. Just cache.clear() after every test, note in docs, call it a day.
4. Do nothing i.e. keep status quo.

Speaking personally, any of 1-3 are better than #4. I also wonder if #3 is 
worth considering. It's certainly the easiest, and part of me wonders if 
objections to it are purely hypothetical. 

Anyhow, thanks for giving me your feedback Jacob and obviously thanks for your 
alternative proposal. I'd be happy to pitch in and work on that idea if that's 
what you guys all lean toward. In the meantime I'd be interested in other 
opinions as well.

Cheers,
Jim

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-developers@googlegroups.com.
To unsubscribe from this group, send email to 
django-developers+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en.

Reply via email to