Re: Changing settings per test
I was going to propose the same thing Santiago. Signals seem like the ideal candidate to solve that problem. -- David Cramer http://www.davidcramer.net On Fri, Nov 5, 2010 at 4:57 AM, Santiago Perez wrote: >> * Settings that are internally cached. For example, anything that >> modifies INSTALLED_APPS. >> >> * Settings that need to make call to reset state affected by loading >> new new group of settings. For example, if you change TZ, you need to >> call "time.tzset()" on teardown to reset the timezone. Similarly for >> deactivating translations if you change USE_I18N. >> >> * Settings that need to be removed, rather that set to None. Again, >> TZ is an example here -- there is a difference between "TZ exists and >> is None" and "TZ doesn't exist". > > Isn't it be possible to change the setattr of settings so that when ever a > setting is changed, a signal is activated and those who cache or need to > trigger actions such as time.tzset() can hook to those signals to perform > those actions? If every setting is ready to be updated then a simple > decorator should be much simpler. These hooks could even allow the owner of > a setting to raise an exception when a setting is modified if it is truly > impossible to change it at run-time. > > -- > You received this message because you are subscribed to the Google Groups > "Django developers" group. > To post to this group, send email to django-develop...@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. > -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-develop...@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.
Re: Changing settings per test
> > * Settings that are internally cached. For example, anything that > modifies INSTALLED_APPS. > > * Settings that need to make call to reset state affected by loading > new new group of settings. For example, if you change TZ, you need to > call "time.tzset()" on teardown to reset the timezone. Similarly for > deactivating translations if you change USE_I18N. > > * Settings that need to be removed, rather that set to None. Again, > TZ is an example here -- there is a difference between "TZ exists and > is None" and "TZ doesn't exist". > Isn't it be possible to change the setattr of settings so that when ever a setting is changed, a signal is activated and those who cache or need to trigger actions such as time.tzset() can hook to those signals to perform those actions? If every setting is ready to be updated then a simple decorator should be much simpler. These hooks could even allow the owner of a setting to raise an exception when a setting is modified if it is truly impossible to change it at run-time. -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-develop...@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.
Re: Changing settings per test
On Nov 5, 2:18 am, Alex Gaynor wrote: > def setUp(self): > self.old_SETTING = getattr(settings, "SETING", _missing) > > def tearDown(self): > if self.old_SETTING is _missing: > del settings.SETTING" > else: > settings.SETTING = self.old_SETTING How about introducing a new function in settings: change_setting(name, new_value) which returns old setting or a marker when there is nothing configured for that value. This function would clear the caches if the setting is cached somewhere, and also handle the env updates for timezone or other settings needing that. Clearing caches is a problem that is not handled well at all in the Django test suite. You could then revert the setting using the same function. This would again handle clearing the caches & env handling. If passed in new_value was the marker for nothing, then the setting would be deleted. Using those functions setting changes would be done like this: def setUp(self): self.old_SETTING = settings.change_setting("SETTING", new_val) # or if you want just to store the setting and change it later in the actual tests # self.old_SETTING = settings.change_setting("SETTING") # will just fetch the old setting, or the marker for missing setting def tearDown(self): settings.change_setting("SETTING", self.old_SETTING) And you would not need to care if the setting was cached somewhere, the change_setting function will take care of that. I don't know if it would be good to have also settings.load_defaults (returns a dict containing all the old settings, loads global_settings). This would need a reverse function, I can't think of a good name for it, settings.revert_load_defaults(old_settings_dict) or something... The append case could have still another function, append_setting(name, value), returning old list (or marker if nothing) and inserting the new value in the list. Reverting would be just change_setting(name, append_setting_ret_val). Handling what needs to be done when changing a setting could be signal based (register_setting_change_listener), this would allow using the same mechanism for settings used by apps not in core. Of course, there could also be decorators which would use these functions... - Anssi -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-develop...@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.
Re: Changing settings per test
On Fri, Nov 5, 2010 at 8:18 AM, Alex Gaynor wrote: > On Thu, Nov 4, 2010 at 8:14 PM, Russell Keith-Magee > wrote: >> So - tl;dr. love the idea. However, we need to handle the edge cases >> if it's going to be added to Django trunk, and replacing Django's own >> usage of the ad-hoc pattern is as good a test as any that we've >> tackled the edge cases. > > I think it's a little cavalier to see what we do is ad-hoc. Sure > there are a bunch of tests with: > > def setUp(self): > self.old_SETTING = getattr(settings, "SETING", _missing) > > def tearDown(self): > if self.old_SETTING is _missing: > del settings.SETTING" > else: > settings.SETTING = self.old_SETTING > > but how else would you write that? That's the whole point of setUp > and tearDown, and I can't think of a more succinct formulation of that > that covers all of the cases (assign/reset attribute, append/insert, > set env variable, etc.). Ad hoc is from the Latin "For this". Ad-hoc doens't mean the pattern is bad or wrong, it just means it isn't generalized. We manually reproduce variations of the pattern you describe every time we need it, rather than having a generalized framework level tool for handling settings. I think "ad hoc" is a pretty apt description of what Django's test suite does. Yours, Russ Magee %-) -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-develop...@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.
Re: Changing settings per test
On Thu, Nov 4, 2010 at 8:14 PM, Russell Keith-Magee wrote: > On Fri, Nov 5, 2010 at 3:46 AM, David Cramer wrote: >> A common behavior I seem to have is the need to tweak the settings >> object for certain test cases. The other case is that in many cases we >> were (are?) relying on settings being configured a certain way for the >> Django tests to even work. I brought this up in #django-dev a while >> back, but wanted to open it up to further discussion. >> >> Let me start with an example test: >> >> def test_with_awesome_setting(self): >> _orig = getattr(settings, 'AWESOME', None) >> settings.AWESOME = True >> >> # do my test >> ... >> >> settings.AWESOME = _orig >> >> So the obvious problem for me here is that I'm repeating this same >> code flow in a lot of situations. Ignoring the fact that it's ugly, >> it's just not fun mangling with settings like this (at least, not fun >> having to reset the values). >> >> My proposal is to include some kind of utility within the test suite >> which would make this easier. There's a couple ways I could see this >> working: >> >> 1. The settings object could be copied and reset on each case. >> 2. The settings object could be replaced with a Proxy which stores a >> copy of any value changed since reset, and returns that value if >> present. It could then simply just be reset (clear the proxy's dict) >> on each setUp. >> >> Anyways, I'd love to hear how others have dealt with this and any >> other possible solutions. > > Love the idea in general. Django's own test suite is full of this > pattern (or buggy partial implementations of it), which is a prime > indication that we should providing something at the framework level > to make this easy to do. > > The devil is in the detail. If you look at the ways Django uses this > pattern, there are lots of edge cases that need to be handled. For > example: > > * Some settings are lists, and the test case needs to > append/prepend/insert into that existing list, rather than overwriting > a list. An example of this is adding an extra context processor for > test purposes. Yes, this could be handled by manually specifying the > full list, but it's a fairly common pattern, so it would be nice to > have a quick way to represent the pattern. > > * Settings that are internally cached. For example, anything that > modifies INSTALLED_APPS. > > * Settings that need to make call to reset state affected by loading > new new group of settings. For example, if you change TZ, you need to > call "time.tzset()" on teardown to reset the timezone. Similarly for > deactivating translations if you change USE_I18N. > > * Settings that need to be removed, rather that set to None. Again, > TZ is an example here -- there is a difference between "TZ exists and > is None" and "TZ doesn't exist". > > I've probably missed a couple of other edge cases; it would be worth > doing an audit of Django's test suite to see all the places that we've > used this pattern, and the workarounds we've had to use to clean up > after it. > > There's also the matter of making this system easy to use in the > practical sense. The context manager approach that have been given so > far in this thread is a nice syntactic fits, but miss one big use case > -- modifying settings for *every* test in a TestCase. This is the way > that settings changes are used right now in Django's test suite. The > provided decorator has the same limitation, but it shouldn't be too > hard to modify it to be a class-or-function decorator. > > Lastly, a persistent source of bugs in Django's own test suite is > having complete settings isolation. If you're doing true unit tests, > it's not enough to just replace one or two settings -- you have to > clear the decks to make sure that the user's settings file doesn't > provide an environment that breaks your test. This manifests itself as > tests for contrib apps that pass fine in Django's own suite, but fail > when an end user deployes the app -- for example, you deploy > contrib.auth in an environment that doesn't have the login URLs > deployed, and your tests fail; or you deploy contrib.flatpages but > don't deploy contrib.sites, and the tests fail. > > Over time, we've cleaned up these issues as we've found them, but the > real fix is to make sure a test runs in a completely clean settings > environment. That is, we reset settings to a baseline > (global_settings.py would be a an obvious candidate) rather than just > tinkering with the one or two settings that we think are important. > > The "Clear the decks" approach is a different use case to "just change > these three settings", but it's closely related, and worth tackling at > the same time, IMHO. > > So - tl;dr. love the idea. However, we need to handle the edge cases > if it's going to be added to Django trunk, and replacing Django's own > usage of the ad-hoc pattern is as good a test as any that we've > tackled the edge cases. > > Yours, > Russ Magee %-) > > -- > You r
Re: Changing settings per test
On Fri, Nov 5, 2010 at 3:46 AM, David Cramer wrote: > A common behavior I seem to have is the need to tweak the settings > object for certain test cases. The other case is that in many cases we > were (are?) relying on settings being configured a certain way for the > Django tests to even work. I brought this up in #django-dev a while > back, but wanted to open it up to further discussion. > > Let me start with an example test: > > def test_with_awesome_setting(self): > _orig = getattr(settings, 'AWESOME', None) > settings.AWESOME = True > > # do my test > ... > > settings.AWESOME = _orig > > So the obvious problem for me here is that I'm repeating this same > code flow in a lot of situations. Ignoring the fact that it's ugly, > it's just not fun mangling with settings like this (at least, not fun > having to reset the values). > > My proposal is to include some kind of utility within the test suite > which would make this easier. There's a couple ways I could see this > working: > > 1. The settings object could be copied and reset on each case. > 2. The settings object could be replaced with a Proxy which stores a > copy of any value changed since reset, and returns that value if > present. It could then simply just be reset (clear the proxy's dict) > on each setUp. > > Anyways, I'd love to hear how others have dealt with this and any > other possible solutions. Love the idea in general. Django's own test suite is full of this pattern (or buggy partial implementations of it), which is a prime indication that we should providing something at the framework level to make this easy to do. The devil is in the detail. If you look at the ways Django uses this pattern, there are lots of edge cases that need to be handled. For example: * Some settings are lists, and the test case needs to append/prepend/insert into that existing list, rather than overwriting a list. An example of this is adding an extra context processor for test purposes. Yes, this could be handled by manually specifying the full list, but it's a fairly common pattern, so it would be nice to have a quick way to represent the pattern. * Settings that are internally cached. For example, anything that modifies INSTALLED_APPS. * Settings that need to make call to reset state affected by loading new new group of settings. For example, if you change TZ, you need to call "time.tzset()" on teardown to reset the timezone. Similarly for deactivating translations if you change USE_I18N. * Settings that need to be removed, rather that set to None. Again, TZ is an example here -- there is a difference between "TZ exists and is None" and "TZ doesn't exist". I've probably missed a couple of other edge cases; it would be worth doing an audit of Django's test suite to see all the places that we've used this pattern, and the workarounds we've had to use to clean up after it. There's also the matter of making this system easy to use in the practical sense. The context manager approach that have been given so far in this thread is a nice syntactic fits, but miss one big use case -- modifying settings for *every* test in a TestCase. This is the way that settings changes are used right now in Django's test suite. The provided decorator has the same limitation, but it shouldn't be too hard to modify it to be a class-or-function decorator. Lastly, a persistent source of bugs in Django's own test suite is having complete settings isolation. If you're doing true unit tests, it's not enough to just replace one or two settings -- you have to clear the decks to make sure that the user's settings file doesn't provide an environment that breaks your test. This manifests itself as tests for contrib apps that pass fine in Django's own suite, but fail when an end user deployes the app -- for example, you deploy contrib.auth in an environment that doesn't have the login URLs deployed, and your tests fail; or you deploy contrib.flatpages but don't deploy contrib.sites, and the tests fail. Over time, we've cleaned up these issues as we've found them, but the real fix is to make sure a test runs in a completely clean settings environment. That is, we reset settings to a baseline (global_settings.py would be a an obvious candidate) rather than just tinkering with the one or two settings that we think are important. The "Clear the decks" approach is a different use case to "just change these three settings", but it's closely related, and worth tackling at the same time, IMHO. So - tl;dr. love the idea. However, we need to handle the edge cases if it's going to be added to Django trunk, and replacing Django's own usage of the ad-hoc pattern is as good a test as any that we've tackled the edge cases. Yours, Russ Magee %-) -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-develop...@googlegroups.com. To unsubscribe from this group, send email to django-dev
Re: Changing settings per test
On 4 November 2010 22:30, David Cramer wrote: > Agree with Alex. We're considering moving more towards decorating > views rather than class attributes. I'm not sure of the performance > implications of many classes vs decorating functions on a large class > instead, but it just seems to make more sense in some cases. What exactly do you mean by "decorating views" in this context ? > On Thu, Nov 4, 2010 at 2:26 PM, Alex Gaynor wrote: >> >> Well, there's no reason the decorator couldn't be used as a class >> decorator (on 2.6 and above). I'll admit that the settings attribute >> on TestCase is more consistant with how we've handled other things >> (urls, fixtures), however, for whatever reason I'm not a fan, as it >> forces you to split up tests that should logically be grouped on a >> single class. You can alter the setting both with a class attribute/decorator and then alter it some more for a particular test. For example (excuse the horrible naming): @alter_class_settings(USE_L10N=True, OTHER_SETTING=False): class MyTest(TestCase): # 20 tests that rely on L10N @alter_test_settings(USE_L10N=False) def test_no_localization(self): pass I meant the class-level decorator/attribute as a shortcut for a common case. I agree that splitting tests just because of settings is bad, but repeating the same decorator all over the test case is bad too, imho. Here[1] is an implementation of a context manager and a class decorator based on the function decorator that David provided. Does it look reasonable ? [1]: https://gist.github.com/663367 -- Łukasz Rekucki -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-develop...@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.
Re: Changing settings per test
Btw, not all settings can be patched just by overriding values because some of them are cached and the change of the value doesn't change django behaviour (e.g. TEMPLATE_CONTEXT_PROCESSORS option behaves like this). On 5 ноя, 02:23, Łukasz Rekucki wrote: > Funny, I had exactly the same problem today at work while refactoring > my application's tests suite :). > > Currently, I'm using a pair of save/restore functions: save() monkey > patches the settings module and returns a dictionary of old values, > restore() puts back the old values based on the dictionary. I usually > put this in setUp/tearDown so I don't have to repeat in every test. I > was about to propose that > Django's TestCase should do something similar by default. > > Both the decorator and context processor are very useful, but having > something to set values for the whole test case instead of a single > test or a block of code would be great too. I was thinking about > something in line of: > > class EmailTestCase(TestCase): > settings = dict(DEFAULT_FROM_EMAIL="webmas...@example.com") > > On 4 November 2010 21:11, David Cramer wrote: > > > > > > > With a decorator approach here's what I whipped up: > > > (This is dry code) > > > def with_settings(**overrides): > > """Allows you to define settings that are required for this > > function to work""" > > NotDefined = object() > > def wrapped(func): > > �...@wraps(func) > > def _with_settings(*args, **kwargs): > > _orig = {} > > for k, v in overrides.iteritems(): > > _orig[k] = getattr(settings, k, NotDefined) > > > try: > > func(*args, **kwargs) > > finally: > > for k, v in _orig.iteritems(): > > if v is NotDefined: > > delattr(settings, k) > > else: > > setattr(settings, k, v) > > return _with_settings > > return wrapped > > > I'm not familiar with the context managers, but I imagine those would > > solve things like adjusting CONTEXT_PROCESSORS. > > > On Thu, Nov 4, 2010 at 1:06 PM, Dan Fairs wrote: > > >>> Let me start with an example test: > > >>> def test_with_awesome_setting(self): > >>> _orig = getattr(settings, 'AWESOME', None) > >>> settings.AWESOME = True > > >>> # do my test > >>> ... > > >>> settings.AWESOME = _orig > > >> Pedant: there's a small bug above which has bitten me before doing a > >> similar thing - settings.AWESOME ends up set to None after the test has > >> run if it didn't exist before. > > >>> Anyways, I'd love to hear how others have dealt with this and any > >>> other possible solutions. > > >> I've used Michael Foord's Mock library to patch a setting for the duration > >> of a test case. Chris Withers' testfixtures library also has some sugar to > >> provide a context manager approach, though I haven't used that in a little > >> while. > > >> Cheers, > >> Dan > > >> -- > >> Dan Fairs | dan.fa...@gmail.com |www.fezconsulting.com > > >> -- > >> You received this message because you are subscribed to the Google Groups > >> "Django developers" group. > >> To post to this group, send email to django-develop...@googlegroups.com. > >> To unsubscribe from this group, send email to > >> django-developers+unsubscr...@googlegroups.com. > >> For more options, visit this group > >> athttp://groups.google.com/group/django-developers?hl=en. > > > -- > > You received this message because you are subscribed to the Google Groups > > "Django developers" group. > > To post to this group, send email to django-develop...@googlegroups.com. > > To unsubscribe from this group, send email to > > django-developers+unsubscr...@googlegroups.com. > > For more options, visit this group > > athttp://groups.google.com/group/django-developers?hl=en. > > -- > Łukasz Rekucki -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-develop...@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.
Re: Changing settings per test
On Nov 4, 4:26 pm, Alex Gaynor wrote: > 2010/11/4 Łukasz Rekucki : > > > > > > > Funny, I had exactly the same problem today at work while refactoring > > my application's tests suite :). > > > Currently, I'm using a pair of save/restore functions: save() monkey > > patches the settings module and returns a dictionary of old values, > > restore() puts back the old values based on the dictionary. I usually > > put this in setUp/tearDown so I don't have to repeat in every test. I > > was about to propose that > > Django's TestCase should do something similar by default. > > > Both the decorator and context processor are very useful, but having > > something to set values for the whole test case instead of a single > > test or a block of code would be great too. I was thinking about > > something in line of: > > > class EmailTestCase(TestCase): > > settings = dict(DEFAULT_FROM_EMAIL="webmas...@example.com") > > > On 4 November 2010 21:11, David Cramer wrote: > >> With a decorator approach here's what I whipped up: > > >> (This is dry code) > > >> def with_settings(**overrides): > >> """Allows you to define settings that are required for this > >> function to work""" > >> NotDefined = object() > >> def wrapped(func): > >> �...@wraps(func) > >> def _with_settings(*args, **kwargs): > >> _orig = {} > >> for k, v in overrides.iteritems(): > >> _orig[k] = getattr(settings, k, NotDefined) > > >> try: > >> func(*args, **kwargs) > >> finally: > >> for k, v in _orig.iteritems(): > >> if v is NotDefined: > >> delattr(settings, k) > >> else: > >> setattr(settings, k, v) > >> return _with_settings > >> return wrapped > > >> I'm not familiar with the context managers, but I imagine those would > >> solve things like adjusting CONTEXT_PROCESSORS. > > >> On Thu, Nov 4, 2010 at 1:06 PM, Dan Fairs wrote: > > Let me start with an example test: > > def test_with_awesome_setting(self): > _orig = getattr(settings, 'AWESOME', None) > settings.AWESOME = True > > # do my test > ... > > settings.AWESOME = _orig > > >>> Pedant: there's a small bug above which has bitten me before doing a > >>> similar thing - settings.AWESOME ends up set to None after the test has > >>> run if it didn't exist before. > > Anyways, I'd love to hear how others have dealt with this and any > other possible solutions. > > >>> I've used Michael Foord's Mock library to patch a setting for the > >>> duration of a test case. Chris Withers' testfixtures library also has > >>> some sugar to provide a context manager approach, though I haven't used > >>> that in a little while. > > >>> Cheers, > >>> Dan > > >>> -- > >>> Dan Fairs | dan.fa...@gmail.com |www.fezconsulting.com > > >>> -- > >>> You received this message because you are subscribed to the Google Groups > >>> "Django developers" group. > >>> To post to this group, send email to django-develop...@googlegroups.com. > >>> To unsubscribe from this group, send email to > >>> django-developers+unsubscr...@googlegroups.com. > >>> For more options, visit this group > >>> athttp://groups.google.com/group/django-developers?hl=en. > > >> -- > >> You received this message because you are subscribed to the Google Groups > >> "Django developers" group. > >> To post to this group, send email to django-develop...@googlegroups.com. > >> To unsubscribe from this group, send email to > >> django-developers+unsubscr...@googlegroups.com. > >> For more options, visit this group > >> athttp://groups.google.com/group/django-developers?hl=en. > > > -- > > Łukasz Rekucki > > > -- > > You received this message because you are subscribed to the Google Groups > > "Django developers" group. > > To post to this group, send email to django-develop...@googlegroups.com. > > To unsubscribe from this group, send email to > > django-developers+unsubscr...@googlegroups.com. > > For more options, visit this group > > athttp://groups.google.com/group/django-developers?hl=en. > > Well, there's no reason the decorator couldn't be used as a class > decorator (on 2.6 and above). I'll admit that the settings attribute > on TestCase is more consistant with how we've handled other things > (urls, fixtures), however, for whatever reason I'm not a fan, as it > forces you to split up tests that should logically be grouped on a > single class. > > Alex > > -- > "I disapprove of what you say, but I will defend to the death your > right to say it." -- Voltaire > "The people's good is the highest law." -- Cicero > "Code can always be simpler than you think, but never as simple as you > want" -- Me What if it worked like self.client. So self.settings would be the current settings with ability to overwrite values in your tests then self.settings could be reset on each
Re: Changing settings per test
Agree with Alex. We're considering moving more towards decorating views rather than class attributes. I'm not sure of the performance implications of many classes vs decorating functions on a large class instead, but it just seems to make more sense in some cases. Here's a working (we're now using it) version of the previous decorator: def with_settings(**overrides): """Allows you to define settings that are required for this function to work""" NotDefined = object() def wrapped(func): @wraps(func) def _with_settings(*args, **kwargs): _orig = {} for k, v in overrides.iteritems(): _orig[k] = getattr(settings, k, NotDefined) setattr(settings, k, v) try: func(*args, **kwargs) finally: for k, v in _orig.iteritems(): if v is NotDefined: delattr(settings, k) else: setattr(settings, k, v) return _with_settings return wrapped -- David Cramer http://www.davidcramer.net On Thu, Nov 4, 2010 at 2:26 PM, Alex Gaynor wrote: > 2010/11/4 Łukasz Rekucki : >> Funny, I had exactly the same problem today at work while refactoring >> my application's tests suite :). >> >> Currently, I'm using a pair of save/restore functions: save() monkey >> patches the settings module and returns a dictionary of old values, >> restore() puts back the old values based on the dictionary. I usually >> put this in setUp/tearDown so I don't have to repeat in every test. I >> was about to propose that >> Django's TestCase should do something similar by default. >> >> Both the decorator and context processor are very useful, but having >> something to set values for the whole test case instead of a single >> test or a block of code would be great too. I was thinking about >> something in line of: >> >> class EmailTestCase(TestCase): >> settings = dict(DEFAULT_FROM_EMAIL="webmas...@example.com") >> >> On 4 November 2010 21:11, David Cramer wrote: >>> With a decorator approach here's what I whipped up: >>> >>> (This is dry code) >>> >>> def with_settings(**overrides): >>> """Allows you to define settings that are required for this >>> function to work""" >>> NotDefined = object() >>> def wrapped(func): >>> �...@wraps(func) >>> def _with_settings(*args, **kwargs): >>> _orig = {} >>> for k, v in overrides.iteritems(): >>> _orig[k] = getattr(settings, k, NotDefined) >>> >>> try: >>> func(*args, **kwargs) >>> finally: >>> for k, v in _orig.iteritems(): >>> if v is NotDefined: >>> delattr(settings, k) >>> else: >>> setattr(settings, k, v) >>> return _with_settings >>> return wrapped >>> >>> I'm not familiar with the context managers, but I imagine those would >>> solve things like adjusting CONTEXT_PROCESSORS. >>> >>> On Thu, Nov 4, 2010 at 1:06 PM, Dan Fairs wrote: > Let me start with an example test: > > def test_with_awesome_setting(self): > _orig = getattr(settings, 'AWESOME', None) > settings.AWESOME = True > > # do my test > ... > > settings.AWESOME = _orig > Pedant: there's a small bug above which has bitten me before doing a similar thing - settings.AWESOME ends up set to None after the test has run if it didn't exist before. > Anyways, I'd love to hear how others have dealt with this and any > other possible solutions. I've used Michael Foord's Mock library to patch a setting for the duration of a test case. Chris Withers' testfixtures library also has some sugar to provide a context manager approach, though I haven't used that in a little while. Cheers, Dan -- Dan Fairs | dan.fa...@gmail.com | www.fezconsulting.com -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-develop...@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. >>> >>> -- >>> You received this message because you are subscribed to the Google Groups >>> "Django developers" group. >>> To post to this group, send email to django-develop...@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. >>> >>> >> >> >> >> -- >> Łukasz Rekucki >> >> -- >> You received this message because you are subscribed
Re: Changing settings per test
2010/11/4 Łukasz Rekucki : > Funny, I had exactly the same problem today at work while refactoring > my application's tests suite :). > > Currently, I'm using a pair of save/restore functions: save() monkey > patches the settings module and returns a dictionary of old values, > restore() puts back the old values based on the dictionary. I usually > put this in setUp/tearDown so I don't have to repeat in every test. I > was about to propose that > Django's TestCase should do something similar by default. > > Both the decorator and context processor are very useful, but having > something to set values for the whole test case instead of a single > test or a block of code would be great too. I was thinking about > something in line of: > > class EmailTestCase(TestCase): > settings = dict(DEFAULT_FROM_EMAIL="webmas...@example.com") > > On 4 November 2010 21:11, David Cramer wrote: >> With a decorator approach here's what I whipped up: >> >> (This is dry code) >> >> def with_settings(**overrides): >> """Allows you to define settings that are required for this >> function to work""" >> NotDefined = object() >> def wrapped(func): >> �...@wraps(func) >> def _with_settings(*args, **kwargs): >> _orig = {} >> for k, v in overrides.iteritems(): >> _orig[k] = getattr(settings, k, NotDefined) >> >> try: >> func(*args, **kwargs) >> finally: >> for k, v in _orig.iteritems(): >> if v is NotDefined: >> delattr(settings, k) >> else: >> setattr(settings, k, v) >> return _with_settings >> return wrapped >> >> I'm not familiar with the context managers, but I imagine those would >> solve things like adjusting CONTEXT_PROCESSORS. >> >> On Thu, Nov 4, 2010 at 1:06 PM, Dan Fairs wrote: >>> Let me start with an example test: def test_with_awesome_setting(self): _orig = getattr(settings, 'AWESOME', None) settings.AWESOME = True # do my test ... settings.AWESOME = _orig >>> >>> Pedant: there's a small bug above which has bitten me before doing a >>> similar thing - settings.AWESOME ends up set to None after the test has >>> run if it didn't exist before. >>> Anyways, I'd love to hear how others have dealt with this and any other possible solutions. >>> >>> I've used Michael Foord's Mock library to patch a setting for the duration >>> of a test case. Chris Withers' testfixtures library also has some sugar to >>> provide a context manager approach, though I haven't used that in a little >>> while. >>> >>> Cheers, >>> Dan >>> >>> -- >>> Dan Fairs | dan.fa...@gmail.com | www.fezconsulting.com >>> >>> >>> -- >>> You received this message because you are subscribed to the Google Groups >>> "Django developers" group. >>> To post to this group, send email to django-develop...@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. >>> >>> >> >> -- >> You received this message because you are subscribed to the Google Groups >> "Django developers" group. >> To post to this group, send email to django-develop...@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. >> >> > > > > -- > Łukasz Rekucki > > -- > You received this message because you are subscribed to the Google Groups > "Django developers" group. > To post to this group, send email to django-develop...@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. > > Well, there's no reason the decorator couldn't be used as a class decorator (on 2.6 and above). I'll admit that the settings attribute on TestCase is more consistant with how we've handled other things (urls, fixtures), however, for whatever reason I'm not a fan, as it forces you to split up tests that should logically be grouped on a single class. Alex -- "I disapprove of what you say, but I will defend to the death your right to say it." -- Voltaire "The people's good is the highest law." -- Cicero "Code can always be simpler than you think, but never as simple as you want" -- Me -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-develop...@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-developer
Re: Changing settings per test
Funny, I had exactly the same problem today at work while refactoring my application's tests suite :). Currently, I'm using a pair of save/restore functions: save() monkey patches the settings module and returns a dictionary of old values, restore() puts back the old values based on the dictionary. I usually put this in setUp/tearDown so I don't have to repeat in every test. I was about to propose that Django's TestCase should do something similar by default. Both the decorator and context processor are very useful, but having something to set values for the whole test case instead of a single test or a block of code would be great too. I was thinking about something in line of: class EmailTestCase(TestCase): settings = dict(DEFAULT_FROM_EMAIL="webmas...@example.com") On 4 November 2010 21:11, David Cramer wrote: > With a decorator approach here's what I whipped up: > > (This is dry code) > > def with_settings(**overrides): > """Allows you to define settings that are required for this > function to work""" > NotDefined = object() > def wrapped(func): > �...@wraps(func) > def _with_settings(*args, **kwargs): > _orig = {} > for k, v in overrides.iteritems(): > _orig[k] = getattr(settings, k, NotDefined) > > try: > func(*args, **kwargs) > finally: > for k, v in _orig.iteritems(): > if v is NotDefined: > delattr(settings, k) > else: > setattr(settings, k, v) > return _with_settings > return wrapped > > I'm not familiar with the context managers, but I imagine those would > solve things like adjusting CONTEXT_PROCESSORS. > > On Thu, Nov 4, 2010 at 1:06 PM, Dan Fairs wrote: >> >>> Let me start with an example test: >>> >>> def test_with_awesome_setting(self): >>> _orig = getattr(settings, 'AWESOME', None) >>> settings.AWESOME = True >>> >>> # do my test >>> ... >>> >>> settings.AWESOME = _orig >>> >> >> Pedant: there's a small bug above which has bitten me before doing a similar >> thing - settings.AWESOME ends up set to None after the test has run if it >> didn't exist before. >> >>> Anyways, I'd love to hear how others have dealt with this and any >>> other possible solutions. >> >> I've used Michael Foord's Mock library to patch a setting for the duration >> of a test case. Chris Withers' testfixtures library also has some sugar to >> provide a context manager approach, though I haven't used that in a little >> while. >> >> Cheers, >> Dan >> >> -- >> Dan Fairs | dan.fa...@gmail.com | www.fezconsulting.com >> >> >> -- >> You received this message because you are subscribed to the Google Groups >> "Django developers" group. >> To post to this group, send email to django-develop...@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. >> >> > > -- > You received this message because you are subscribed to the Google Groups > "Django developers" group. > To post to this group, send email to django-develop...@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. > > -- Łukasz Rekucki -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-develop...@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.
Re: Changing settings per test
With a decorator approach here's what I whipped up: (This is dry code) def with_settings(**overrides): """Allows you to define settings that are required for this function to work""" NotDefined = object() def wrapped(func): @wraps(func) def _with_settings(*args, **kwargs): _orig = {} for k, v in overrides.iteritems(): _orig[k] = getattr(settings, k, NotDefined) try: func(*args, **kwargs) finally: for k, v in _orig.iteritems(): if v is NotDefined: delattr(settings, k) else: setattr(settings, k, v) return _with_settings return wrapped I'm not familiar with the context managers, but I imagine those would solve things like adjusting CONTEXT_PROCESSORS. On Thu, Nov 4, 2010 at 1:06 PM, Dan Fairs wrote: > >> Let me start with an example test: >> >> def test_with_awesome_setting(self): >> _orig = getattr(settings, 'AWESOME', None) >> settings.AWESOME = True >> >> # do my test >> ... >> >> settings.AWESOME = _orig >> > > Pedant: there's a small bug above which has bitten me before doing a similar > thing - settings.AWESOME ends up set to None after the test has run if it > didn't exist before. > >> Anyways, I'd love to hear how others have dealt with this and any >> other possible solutions. > > I've used Michael Foord's Mock library to patch a setting for the duration of > a test case. Chris Withers' testfixtures library also has some sugar to > provide a context manager approach, though I haven't used that in a little > while. > > Cheers, > Dan > > -- > Dan Fairs | dan.fa...@gmail.com | www.fezconsulting.com > > > -- > You received this message because you are subscribed to the Google Groups > "Django developers" group. > To post to this group, send email to django-develop...@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. > > -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-develop...@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.
Re: Changing settings per test
> Let me start with an example test: > > def test_with_awesome_setting(self): >_orig = getattr(settings, 'AWESOME', None) >settings.AWESOME = True > ># do my test >... > >settings.AWESOME = _orig > Pedant: there's a small bug above which has bitten me before doing a similar thing - settings.AWESOME ends up set to None after the test has run if it didn't exist before. > Anyways, I'd love to hear how others have dealt with this and any > other possible solutions. I've used Michael Foord's Mock library to patch a setting for the duration of a test case. Chris Withers' testfixtures library also has some sugar to provide a context manager approach, though I haven't used that in a little while. Cheers, Dan -- Dan Fairs | dan.fa...@gmail.com | www.fezconsulting.com -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-develop...@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.
Re: Changing settings per test
On Thu, Nov 4, 2010 at 3:46 PM, David Cramer wrote: > A common behavior I seem to have is the need to tweak the settings > object for certain test cases. The other case is that in many cases we > were (are?) relying on settings being configured a certain way for the > Django tests to even work. I brought this up in #django-dev a while > back, but wanted to open it up to further discussion. > > Let me start with an example test: > > def test_with_awesome_setting(self): > _orig = getattr(settings, 'AWESOME', None) > settings.AWESOME = True > > # do my test > ... > > settings.AWESOME = _orig > > So the obvious problem for me here is that I'm repeating this same > code flow in a lot of situations. Ignoring the fact that it's ugly, > it's just not fun mangling with settings like this (at least, not fun > having to reset the values). > > My proposal is to include some kind of utility within the test suite > which would make this easier. There's a couple ways I could see this > working: > > 1. The settings object could be copied and reset on each case. > 2. The settings object could be replaced with a Proxy which stores a > copy of any value changed since reset, and returns that value if > present. It could then simply just be reset (clear the proxy's dict) > on each setUp. > > Anyways, I'd love to hear how others have dealt with this and any > other possible solutions. > > -- > You received this message because you are subscribed to the Google Groups > "Django developers" group. > To post to this group, send email to django-develop...@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. > > Russ and I discussed having a decorator/context manager for monkey patching settings: @swap_settings(TEMPLATE_DIRS=[fuzzy], DEBUG=True) def test_who_cares(self): self.assertTrue(settings.DEBUG) or with swap_settings(DEBUG=True): self.assertTrue(settings.DEBUG) Alex -- "I disapprove of what you say, but I will defend to the death your right to say it." -- Voltaire "The people's good is the highest law." -- Cicero "Code can always be simpler than you think, but never as simple as you want" -- Me -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-develop...@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.
Changing settings per test
A common behavior I seem to have is the need to tweak the settings object for certain test cases. The other case is that in many cases we were (are?) relying on settings being configured a certain way for the Django tests to even work. I brought this up in #django-dev a while back, but wanted to open it up to further discussion. Let me start with an example test: def test_with_awesome_setting(self): _orig = getattr(settings, 'AWESOME', None) settings.AWESOME = True # do my test ... settings.AWESOME = _orig So the obvious problem for me here is that I'm repeating this same code flow in a lot of situations. Ignoring the fact that it's ugly, it's just not fun mangling with settings like this (at least, not fun having to reset the values). My proposal is to include some kind of utility within the test suite which would make this easier. There's a couple ways I could see this working: 1. The settings object could be copied and reset on each case. 2. The settings object could be replaced with a Proxy which stores a copy of any value changed since reset, and returns that value if present. It could then simply just be reset (clear the proxy's dict) on each setUp. Anyways, I'd love to hear how others have dealt with this and any other possible solutions. -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-develop...@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.