I have the following project structure:
myapp/settings/__init__.py
myapp/settings/base.py
myapp/settings/test.py
Inside base.py I have a setting called REST_FRAMEWORK from the Django REST Framework project. test.py does from myapp.settings.base import * at the top and then adds some database settings of its own. Now, for some reason pytest completely ignores the REST_FRAMEWORK setting even though the test.py file is loaded and I can print the contents of REST_FRAMEWORK inside it. Django works as expected.
If I manually copy the REST_FRAMEWORK setting to the test.py file instead of relying on extending then my tests pass. Any ideas? This seems like such a simple case that I must be missing something but I can't track it down.
pytest 2.7.2 and pytest-django 2.8.0
Are you sure DJANGO_SETTINGS_MODULE is set properly?
Here's my pytest.ini:
[pytest]
DJANGO_SETTINGS_MODULE=myapp.settings.test
norecursedirs=venv .git build
and I've also set it on the command line with --ds=myapp.settings.test to no avail
Ok, strange.
Can you add import pdb; pdb.set_trace() before the import of base and then see where it is being called from (bt), and then step into it to see what is going on.
Here's where it comes from:
$ py.test -vxsk test_public_api
> ~/projects/myapp/settings/test.py(2)<module>()
1 import ipdb;ipdb.set_trace()
----> 2 from myapp.settings.base import * # pylint: disable=W0614,W0401
3
ipdb> bt
~/.virtualenvs/myapp/bin/py.test(11)<module>()
7 from pytest import main
8
9 if __name__ == '__main__':
10 sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
---> 11 sys.exit(main())
~/.virtualenvs/myapp/local/lib/python2.7/site-packages/_pytest/config.py(32)main()
30 """
31 try:
---> 32 config = _prepareconfig(args, plugins)
33 except ConftestImportFailure:
34 e = sys.exc_info()[1]
~/.virtualenvs/myapp/local/lib/python2.7/site-packages/_pytest/config.py(85)_prepareconfig()
83 pluginmanager.register(plugin)
84 return pluginmanager.hook.pytest_cmdline_parse(
---> 85 pluginmanager=pluginmanager, args=args)
86 except Exception:
87 pluginmanager.ensure_shutdown()
~/.virtualenvs/myapp/local/lib/python2.7/site-packages/_pytest/core.py(521)__call__()
519
520 def __call__(self, **kwargs):
--> 521 return self._docall(self.methods, kwargs)
522
523 def callextra(self, methods, **kwargs):
~/.virtualenvs/myapp/local/lib/python2.7/site-packages/_pytest/core.py(528)_docall()
526 def _docall(self, methods, kwargs):
527 return MultiCall(methods, kwargs,
--> 528 firstresult=self.firstresult).execute()
529
530
~/.virtualenvs/myapp/local/lib/python2.7/site-packages/_pytest/core.py(393)execute()
391 args = [all_kwargs[argname] for argname in varnames(method)]
392 if hasattr(method, "hookwrapper"):
--> 393 return wrapped_call(method(*args), self.execute)
394 res = method(*args)
395 if res is not None:
~/.virtualenvs/myapp/local/lib/python2.7/site-packages/_pytest/core.py(107)wrapped_call()
105 except StopIteration:
106 raise_wrapfail(wrap_controller, "did not yield")
--> 107 call_outcome = CallOutcome(func)
108 try:
109 wrap_controller.send(call_outcome)
~/.virtualenvs/myapp/local/lib/python2.7/site-packages/_pytest/core.py(123)__init__()
121 def __init__(self, func):
122 try:
--> 123 self.result = func()
124 except BaseException:
125 self.excinfo = sys.exc_info()
~/.virtualenvs/myapp/local/lib/python2.7/site-packages/_pytest/core.py(394)execute()
392 if hasattr(method, "hookwrapper"):
393 return wrapped_call(method(*args), self.execute)
--> 394 res = method(*args)
395 if res is not None:
396 self.results.append(res)
~/.virtualenvs/myapp/local/lib/python2.7/site-packages/_pytest/config.py(636)pytest_cmdline_parse()
634 def pytest_cmdline_parse(self, pluginmanager, args):
635 assert self == pluginmanager.config, (self, pluginmanager.config)
--> 636 self.parse(args)
637 return self
638
~/.virtualenvs/myapp/local/lib/python2.7/site-packages/_pytest/config.py(746)parse()
744 "can only parse cmdline args at most once per Config object")
745 self._origargs = args
--> 746 self._preparse(args)
747 # XXX deprecated hook:
748 self.hook.pytest_cmdline_preparse(config=self, args=args)
~/.virtualenvs/myapp/local/lib/python2.7/site-packages/_pytest/config.py(718)_preparse()
716 try:
717 self.hook.pytest_load_initial_conftests(early_config=self,
--> 718 args=args, parser=self._parser)
719 except ConftestImportFailure:
720 e = sys.exc_info()[1]
~/.virtualenvs/myapp/local/lib/python2.7/site-packages/_pytest/core.py(521)__call__()
519
520 def __call__(self, **kwargs):
--> 521 return self._docall(self.methods, kwargs)
522
523 def callextra(self, methods, **kwargs):
~/.virtualenvs/myapp/local/lib/python2.7/site-packages/_pytest/core.py(528)_docall()
526 def _docall(self, methods, kwargs):
527 return MultiCall(methods, kwargs,
--> 528 firstresult=self.firstresult).execute()
529
530
~/.virtualenvs/myapp/local/lib/python2.7/site-packages/_pytest/core.py(393)execute()
391 args = [all_kwargs[argname] for argname in varnames(method)]
392 if hasattr(method, "hookwrapper"):
--> 393 return wrapped_call(method(*args), self.execute)
394 res = method(*args)
395 if res is not None:
~/.virtualenvs/myapp/local/lib/python2.7/site-packages/_pytest/core.py(107)wrapped_call()
105 except StopIteration:
106 raise_wrapfail(wrap_controller, "did not yield")
--> 107 call_outcome = CallOutcome(func)
108 try:
109 wrap_controller.send(call_outcome)
~/.virtualenvs/myapp/local/lib/python2.7/site-packages/_pytest/core.py(123)__init__()
121 def __init__(self, func):
122 try:
--> 123 self.result = func()
124 except BaseException:
125 self.excinfo = sys.exc_info()
~/.virtualenvs/myapp/local/lib/python2.7/site-packages/_pytest/core.py(394)execute()
392 if hasattr(method, "hookwrapper"):
393 return wrapped_call(method(*args), self.execute)
--> 394 res = method(*args)
395 if res is not None:
396 self.results.append(res)
~/.virtualenvs/myapp/local/lib/python2.7/site-packages/pytest_django/plugin.py(87)pytest_load_initial_conftests()
85 if pytest.__version__[:3] >= "2.4":
86 def pytest_load_initial_conftests(early_config, parser, args):
---> 87 _load_settings(early_config, parser.parse_known_args(args))
88
89
~/.virtualenvs/myapp/local/lib/python2.7/site-packages/pytest_django/plugin.py(79)_load_settings()
77 from django.conf import settings
78 try:
---> 79 settings.DATABASES
80 except ImportError:
81 e = sys.exc_info()[1]
~/.virtualenvs/myapp/local/lib/python2.7/site-packages/django/conf/__init__.py(48)__getattr__()
46 def __getattr__(self, name):
47 if self._wrapped is empty:
---> 48 self._setup(name)
49 return getattr(self._wrapped, name)
50
~/.virtualenvs/myapp/local/lib/python2.7/site-packages/django/conf/__init__.py(44)_setup()
42 % (desc, ENVIRONMENT_VARIABLE))
43
---> 44 self._wrapped = Settings(settings_module)
45
46 def __getattr__(self, name):
~/.virtualenvs/myapp/local/lib/python2.7/site-packages/django/conf/__init__.py(92)__init__()
90 self.SETTINGS_MODULE = settings_module
91
---> 92 mod = importlib.import_module(self.SETTINGS_MODULE)
93
94 tuple_settings = (
/usr/lib/python2.7/importlib/__init__.py(37)import_module()
34 break
35 level += 1
36 name = _resolve_name(name[level:], package, level)
---> 37 __import__(name)
38 return sys.modules[name]
> ~/projects/myapp/settings/test.py(2)<module>()
1 import ipdb;ipdb.set_trace()
----> 2 from myapp.settings.base import * # pylint: disable=W0614,W0401
3
4 DEBUG = False
5 TEMPLATE_DEBUG = DEBUG
and as soon as I step over the import REST_FRAMEWORK is available and has the correct settings from base.py. Once I step through this file and it goes into django settings internals I can get settings.REST_FRAMEWORK and it is also correct. But if I hit c to continue the test it fails with errors indicating it isn't actually running with the values imported from base.py. The values from DATABASES stick, just not from REST_FRAMEWORK.
Can you reduce this to a minimal project with multiple settings files where the issue can be reproduced?
i discovered that the DJANGO_SETTINGS_MODULE value in pytest.ini does not override a DJANGO_SETTINGS_MODULE environment variable. i also have a settings file for testing which is derived from another settings file- pytest is using the base settings file since that's the value in the environment variable.
@smcoll is right. Seems like the ENV value overrides the INI/config value:
For us we're able to have things work via DJANGO_SETTINGS_MODULE=config.settings.test pytest because we're running things locally with Docker/Compose which for us defaults to config.settings.local in the ENV.
I feel like pytest-django is doing the right thing but I also wonder if maybe the config should should outweigh the env?
I just got bit by this issue. I think config should outweigh the env, personally...
The order of discovery is documented here.
An envvar traditionally is stronger than a configuration file, so this is expected behavior, and due to backward compatibility is unlikely to change anyway.
One way to trick this is to use
[pytest]
addopts = --ds=my_settings
instead of the DJANGO_SETTINGS_MODULE setting. A command-line flag takes precedence over anything else.
Most helpful comment
i discovered that the
DJANGO_SETTINGS_MODULEvalue inpytest.inidoes not override aDJANGO_SETTINGS_MODULEenvironment variable. i also have a settings file for testing which is derived from another settings file- pytest is using the base settings file since that's the value in the environment variable.