I am getting the error below:
(testassets)➜ testassets git:(master) ✗ django-admin.py test Creating test database for alias 'default'... E ====================================================================== ERROR: test_get_site_root_with_settings_overrides (app.tests.AssetsTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Volumes/fifteen5cs/testassets/app/tests.py", line 27, in test_get_site_root_with_settings_overrides http_client.get('/') File "/Users/paul/.pyenv/versions/testassets/lib/python2.7/site-packages/django/test/client.py", line 473, in get response = super(Client, self).get(path, data=data, **extra) File "/Users/paul/.pyenv/versions/testassets/lib/python2.7/site-packages/django/test/client.py", line 280, in get return self.request(**r) File "/Users/paul/.pyenv/versions/testassets/lib/python2.7/site-packages/django/test/client.py", line 444, in request six.reraise(*exc_info) File "/Users/paul/.pyenv/versions/testassets/lib/python2.7/site-packages/django/core/handlers/base.py", line 114, in get_response response = wrapped_callback(request, *callback_args, **callback_kwargs) File "/Volumes/fifteen5cs/testassets/app/views.py", line 9, in index context_instance=RequestContext(request)) File "/Users/paul/.pyenv/versions/testassets/lib/python2.7/site-packages/django/shortcuts/init.py", line 29, in render_to_response return HttpResponse(loader.render_to_string(*args, **kwargs), **httpresponse_kwargs) File "/Users/paul/.pyenv/versions/testassets/lib/python2.7/site-packages/django/template/loader.py", line 169, in render_to_string return t.render(context_instance) File "/Users/paul/.pyenv/versions/testassets/lib/python2.7/site-packages/django/template/base.py", line 140, in render return self._render(context) File "/Users/paul/.pyenv/versions/testassets/lib/python2.7/site-packages/django/test/utils.py", line 85, in instrumented_test_render return self.nodelist.render(context) File "/Users/paul/.pyenv/versions/testassets/lib/python2.7/site-packages/django/template/base.py", line 840, in render bit = self.render_node(node, context) File "/Users/paul/.pyenv/versions/testassets/lib/python2.7/site-packages/django/template/debug.py", line 78, in render_node return node.render(context) File "/Users/paul/.pyenv/versions/testassets/lib/python2.7/site-packages/django_assets/templatetags/assets.py", line 72, in render for url in bundle.urls(): File "/Users/paul/.pyenv/versions/testassets/lib/python2.7/site-packages/webassets/bundle.py", line 783, in urls for bundle, extra_filters, new_ctx in self.iterbuild(ctx): File "/Users/paul/.pyenv/versions/testassets/lib/python2.7/site-packages/webassets/bundle.py", line 679, in iterbuild for bundle, _ in self.resolve_contents(ctx): File "/Users/paul/.pyenv/versions/testassets/lib/python2.7/site-packages/webassets/bundle.py", line 233, in resolve_contents result = ctx.resolver.resolve_source(ctx, item) File "/Users/paul/.pyenv/versions/testassets/lib/python2.7/site-packages/webassets/bundle.py", line 50, in getattr return self.getattr(self._parent, item) File "/Users/paul/.pyenv/versions/testassets/lib/python2.7/site-packages/webassets/bundle.py", line 58, in getattr return getattr(object, item) File "/Users/paul/.pyenv/versions/testassets/lib/python2.7/site-packages/webassets/env.py", line 675, in _get_resolver return self._storage['resolver'] File "/Users/paul/.pyenv/versions/testassets/lib/python2.7/site-packages/django_assets/env.py", line 62, in getitem self._transform_key(key)) KeyError: "Django settings doesn't define RESOLVER"
----------------------------------------------------------------------
Ran 1 test in 0.325s
FAILED (errors=1)
Destroying test database for alias 'default'...
I have semi-linked this error to the use of the Django utils function django.test.utils.override_settings
within one of my unit tests (shown below)
1 from django.test.utils import override_settings
2 from django.utils.unittest.case import TestCase
3 from django.test.client import Client
4
5
6 OVERRIDE_SETTINGS = {
7 'DEBUG': True,
8 'ASSETS_DEBUG': True,
9 'ASSETS_AUTO_BUILD': True,
10 'ASSETS_URL_EXPIRE': False,
11 'ASSETS_CACHE': False,
12 'ASSETS_MANIFEST': False,
13 'ASSETS_VERSIONS': False,
14 }
15
16
17 class AssetsTestCase(TestCase):
18 def test_get_site_root_with_settings_overrides(self):
19 http_client = Client()
20 # import pdb;pdb.set_trace()
21 settings_override = override_settings(**OVERRIDE_SETTINGS)
22 settings_override.enable()
23 http_client.get('/')
24 settings_override.disable()
25
26 settings_override.enable()
27 http_client.get('/')
28 settings_override.disable()
(NB. The exception is raised during the second request!)
The code base that I am working on that first presented this issue is too large and private to share so I have stripped down the project to a small amount of code that still creates the issue. That mini project can be found here https://github.com/logston/testassets.
I have spent over two days trying to determine where exactly this error is coming from and why it occurs during the second request but not the first. I have tried a number of permutations of unit tests. Interestingly, if I create a second unit test, one that does not enable settings overrides (eg. like the one below) and name that test such that it runs first during testing, the test suite passes. If I place that same unit test after the test_get_site_root_with_settings_overrides
unit test, both will fail.
def test_get_site_root(self):
http_client = Client()
http_client.get('/')
http_client.get('/')
Any help on this issue would be immensely appreciated.
Finally, the only issue that I can find that speaks of the same or a similar problem is here: https://github.com/miracle2k/django-assets/issues/44
UPDATE 2015/01/12
The issue seems to be related to the use of signals. I have stripped the above failing test down to the following:
from django.test.utils import override_settings
from django.utils.unittest.case import TestCase
from django_assets.env import get_env
class AssetsTestCase(TestCase):
def test(self):
settings_override = override_settings()
settings_override.enable()
get_env().resolver
settings_override.disable()
settings_override.enable()
get_env().resolver
settings_override.disable()
Turns out this issue is due to the fact that the
django_assets.env.env
singleton is not reset after disabling anyoverride_settings
. The fact that this singleton object is not rebuilt between 'settings' objects means that if adjango_assets.env.env
object is built in the context of overridden settings, when those overridden settings are swapped out for the non-overridden settings, any constants added to the interim settings module by the creation of thedjango_assets.env.env
object will be lost.RESOLVER
andASSETS_CACHE
are great examples of constants that will be lost. To avoid this loss, we have to be sure to reset thedjango_assets.env.env
object by callingdjango_assets.env.reset
after a change in settings modules. Callingreset
will force django-assets to reinsert these constants back into the current settings modules the next timedjango_assets.env.get_env
is called.Unfortunately, calling
django_assets.env.reset
causes thedjango_assets.env.env._bundle_names
dictionary to be emptied (nb. really its destroyed and a new one is built). The loss of this dictionary causes errors like the following:To fix this issue, we have to update
django_assets.env._ASSETS_LOADED
toFalse
and remove each app'sassets.py
file fromsys.modules
. We have to update_ASSETS_LOADED
so that django-assets attempts to reimport each app's assets file the next timedjango_assets.env.get_env
is called. The call todjango_assets.env.get_env
will also rebuild theenv._bundle_names
dictionary. Finally, we have to remove each app's assets module from sys.modules. Otherwise,__import__('app.assets')
will not import (read 'execute') the assets module because the assets module is already imported!Thus, a complete solution to this question would be something like the following:
BTW, I am all ears for suggestions on other ways to solve this issue.