initial_data.json not loading before tests - django

I am using django_nose to run my tests. I have a fixture initial_data.json which is an app's folder : my_app/fixtures/initial.json.
The fixture loads fine when using python manage.py syncdb, but doesn't load at all when running the tests python manage.py test my_app (while I guess it should load !?). Any pointer to what could be wrong ?

I quote the official documentation:
Once you've created a fixture and placed it in a fixtures directory in one of your INSTALLED_APPS, you can use it in your unit tests by specifying a fixtures class attribute on your django.test.TestCase subclass:
from django.test import TestCase
from myapp.models import Animal
class AnimalTestCase(TestCase):
fixtures = ['mammals.json', 'birds']
def setUp(self):
# Test definitions as before.
call_setup_methods()
def testFluffyAnimals(self):
# A test that uses the fixtures.
call_some_test_code()

I had similar symptoms but a different solution. I am running my tests through py.test instead of manage.py test.
In appname/fixtures I had initial_data.json and foo.json. My test looked like this:
from django.test import TestCase
class TestFoo(TestCase):
fixtures = ['initial_data.json', 'foo.json']
...
However, when I ran the test initial_data.json was not being loaded, but foo.json was.
For me, the solution was to rename initial_data.json to base.json, and then the fixture was loaded.
I don't think this is the original poster's problem, but posting this so the next person that has the same problem as me can find something. :)

Related

Django executing tests for app not in INSTALLED_APPS

Under my Django project there are a few apps and all of them have unit tests. One of them that I'm working right now is supposed to be included only in dev/stage environments, so I'm enabling it using a environment variable.
When this variable is present it is added to INSTALLED_APPS and it is working just fine, the problem is that Django is executing the tests for this app even when it is not in INSTALLED_APPS, and it fails with the following message:
ImportError: Failed to import test module: debug.tests.unit.test_services`
...(traceback information)...
RuntimeError: Model class debug.models.Email doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.
When I define the app_label in the class Meta of models in this app the error is different, it says it can't find a table, I assume that this is because the app is not in INSTALLED_APPS, so it's migrations are not executed.
OperationalError: no such table: debug_email
I'm not sure why Django executes the tests for all apps, but not it's migrations.
Am I missing something from Django configuration for tests?
https://docs.python.org/3/library/unittest.html#unittest.TestLoader.discover says:
If load_tests exists then discovery does not recurse into the package, load_tests is responsible for loading all tests in the package.
So in the lowest __init__.py in your app which you don't always want run:
from django.apps import apps
def load_tests(loader, tests, pattern):
from django.conf import settings
if apps.is_installed("your_dev_app"):
# Actually load the tests - thanks to #barney-szabolcs
return loader.discover(start_dir=dirname(abspath(__file__)), pattern=pattern)
You need to return the discovered tests in load_tests.
So, adding to #DaveLawrence's answer, the complete code is:
# your_dev_app/__init__.py
from django.apps import apps
from os.path import dirname, abspath
def load_tests(loader, tests, pattern):
"""
loads tests for your_dev_app if it is installed.
"""
from django.conf import settings
if apps.is_installed("your_dev_app"):
return loader.discover(start_dir=dirname(abspath(__file__)), pattern=pattern)
When you run:
python manage.py test
the command will look per default recursive for all files with the pattern test*.py in the working directory. It isn't affected by INSTALLED_APPS in settings.py.
You can specify a certain app to test it:
python manage.py test app_label
or specify a path:
python manage.py test myapp/tests
If you want to exclude some tests you can tag them and use the option --exclude-tag.
Run python manage.py test --help to get information on all options.
The official documentation gives a lot of information on the different possibilities how to run the tests.
EDIT:
If you have apps that are required only in the development environment, but not in the production, you could split your settings.py. One possible solution would be to outsource all development settings into a file local_settings.py and exclude it from versioning or from the production branch, i.e. don't push it in the production environment.
local_settings.py
DEBUG = True
INSTALLED_APPS += (
# Django Debug Toolbar would be for example
# used only in development
'debug_toolbar',
'your dev app',
)
settings.py
try:
from .local_settings import *
except ImportError:
pass

Integrity error when loading fixtures for Selenium testing in Django

I want to load a fixture for my selenium testing. Using fixtures was successful in my initial tests, so I know I am capable of loading the fixtures in my test setup and using them in my tests. I have attempted several approaches.
First, I generated fixtures specific to the models I was testing using dumpdata. An example is below:
python manage.py dumpdata protocols.Step --indent=2 > functional_tests/fixtures/step.json
When used in my test as so:
class SignInTest(FunctionalTest):
fixtures = ['admin_user.json', 'protocol.json', 'step.json',
'protocol_element.json']
def test_login_and_view_user_data(self):
...
I get error:
django.db.utils.IntegrityError: Problem installing fixtures: The row in table 'protocols_protocolelement' with primary key '37' has an invalid foreign key: protocols_protocolelement.element_content_type_id contains a value '41' that does not have a corresponding value in django_content_type.id.
Second attempt involved using all the test data in my tables, but excluding contenttypes:
python manage.py dumpdata --indent=2 -e contenttypes > functional_tests/fixtures/initial_data.json
class SignInTest(FunctionalTest):
fixtures = ['initial_data.json']
...
Getting the error:
django.db.utils.OperationalError: Problem installing fixture '.../mike/mike/functional_tests/fixtures/initial_data.json': Could not load auth.Permission(pk=103): no such table: auth_permission
Next, I tried using natural to show the natural keys:
python manage.py dumpdata --natural -e contenttypes -e auth.Permission --indent=2 > functional_tests/fixtures/initial_data2.json
Only to get the error:
django.db.utils.OperationalError: Problem installing fixture '.../mike/mike/functional_tests/fixtures/initial_data.json': Could not load auth.User(pk=1): no such table: auth_user
Noticing natural was depreciated I tried --natural-foreign and wanted to include user and permission models (I need contenttypes for my models anyway):
python manage.py dumpdata --natural-foreign --indent=2 > functional_tests/fixtures/initial_data3.json
Only to get the error:
django.db.utils.IntegrityError: Problem installing fixture '.../mike/mike/functional_tests/fixtures/initial_data3.json': Could not load contenttypes.ContentType(pk=35): UNIQUE constraint failed: django_content_type.app_label, django_content_type.model
So, any ideas on how to load the fixture so I can run my tests? Is there something simple I'm missing? Thanks!
After some more reading about how Django maintains its own models and such, it is my understanding that Django caches the contenttype, auth.Permission, etc and uses them in testing frameworks (I was using StaticLiveServerTestCase). This means that when I was loading my fixture, it was clashing with the data Django had stored for its own uses causing the integrity error. This is what worked for me:
python manage.py dumpdata -e contenttypes -e admin -e auth.Permission --natural-foreign --indent=2 > functional_tests/fixtures/initial_data4.json
This post has some additional helpful information to help me solve the problem:
Problems with contenttypes when loading a fixture in Django.
All this exercises around dumpdata didn't bring me any result.
But I found this advise:
https://stackoverflow.com/a/3855764/12191031
For some reason reset doesn't work in my code, but it was enough load fixture before every test and flush DB after each one.
So, my solution is this:
from django.core import management
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from selenium import webdriver
class login_page_test(StaticLiveServerTestCase):
#classmethod
def setUpClass(cls):
cls.selenium = webdriver.Firefox()
cls.selenium.implicitly_wait(5)
super(login_page_test, cls).setUpClass()
#classmethod
def tearDownClass(cls):
cls.selenium.quit()
super(login_page_test, cls).tearDownClass()
def setUp(self):
# for every test I load fixture
management.call_command("loaddata", "user-data.yaml", verbosity=0)
super(login_page_test, self).setUp()
def tearDown(self):
# after each test I flush all DB
management.call_command("flush", verbosity=0, interactive=False)
super(login_page_test, self).setUp()
I'm happy )))

Fixtures not loaded during testing

I wrote a unit test checking whether initial data is loaded correctly. However the Node.objects.all().count() always returns 0, thus it seems as the fixtures are not loaded at all. There is no output/error msg in the command line that fixtures are not loaded.
from core.models import Node
class NodeTableTestCase(unittest.TestCase):
fixtures = ['core/core_fixture.json']
def setUp(self):
print "nothing to prepare..."
def testFixture(self):
"""Check if initial data can be loaded correctly"""
self.assertEqual(Node.objects.all().count(), 14)
the fixture core_fixture.json contains 14 nodes and I'm using this fixture as a initial data load into the db using the following command:
python manage.py loaddata core/core_fixture.json
They are located in the folder I provided in the settings.py setting FIXTURE_DIRS.
Found the solution in another thread, answer from John Mee
# Import the TestCase from django.test:
# Bad: import unittest
# Bad: import django.utils.unittest
# Good: import django.test
from django.test import TestCase
class test_something(TestCase):
fixtures = ['one.json', 'two.json']
...
Doing this I got a proper error message, saying that foreign key is violated and I had to also include the fixtures for the app "auth". I exported the needed data with this command:
manage.py dumpdata auth.User auth.Group > usersandgroups.json
Using Unittest I got only the message that loading of fixture data failed, which was not very helpful.
Finally my working test looks like this:
from django.test import TestCase
class NodeTableTestCase2(TestCase):
fixtures = ['auth/auth_usersandgroups_fixture.json','core/core_fixture.json']
def setUp(self):
# Test definitions as before.
print "welcome in setup: while..nothing to setup.."
def testFixture2(self):
"""Check if initial data can be loaded correctly"""
self.assertEqual(Node.objects.all().count(), 11)
When loading fixtures in test cases, I don't think Django allows you to include the directory name. Try changing your fixtures setting to:
fixtures = ['core_fixture.json',]
You might have to change your FIXTURE_DIRS setting as well, to include the core directory.
If you run your tests in verbose mode, you will see the fixture files that Django attempts to load. This should help you debug your configuration.
python manage.py test -v 2
Make sure you have your app listed in INSTALLED_APPS and that your app contains models.py file.

Emulating an app with models in a django unittest

Im writing some code which retrieves info about installed apps, especially defined models, and then does stuff based on that information, but Im having some problems writing a clean, nice unittest. Is there a way to emulate or add an app in unittests without have to run manage.py startproject, manage.py startapp in my testsfolder to have a test app available for unittests?
Sure, try this on for size:
from django.conf import settings
from django.core.management import call_command
from django.test.testcases import TestCase
from django.db.models import loading
class AppTestCase(TestCase):
'''
Adds apps specified in `self.apps` to `INSTALLED_APPS` and
performs a `syncdb` at runtime.
'''
apps = ()
_source_installed_apps = ()
def _pre_setup(self):
super(AppTestCase, self)._pre_setup()
if self.apps:
self._source_installed_apps = settings.INSTALLED_APPS
settings.INSTALLED_APPS = settings.INSTALLED_APPS + self.apps
loading.cache.loaded = False
call_command('syncdb', verbosity=0)
def _post_teardown(self):
super(AppTestCase, self)._post_teardown()
if self._source_installed_apps:
settings.INSTALLED_APPS = self._source_installed_apps
self._source_installed_apps = ()
loading.cache.loaded = False
Your test case would look something like this:
class SomeAppTestCase(AppTestCase):
apps = ('someapp',)
In case you were wondering why, I did an override of _pre_setup() and _post_teardown() so I don't have to bother with calling super() in setUp() and tearDown() in my final test case. Otherwise, this is what I pulled out of Django's test runner. I whipped it up and it worked, although I'm sure that, with closer inspection, you can further optimize it and even avoid calling syncdb every time if it won't conflict with future tests.
EDIT:
So I seem to have gotten out of my way, thinking you need to dynamically add new models. If you've created an app for testing purposes only, here's what you can do to have it discovered during your tests.
In your project directory, create a test.py file that will contain your test settings. It should look something like this:
from settings import *
# registers test app for discovery
INSTALLED_APPS += ('path.to.test.app',)
You can now run your tests with python manage.py test --settings=myproject.test and your app will be in the installed apps.

Loading fixtures in django unit tests

I'm trying to start writing unit tests for django and I'm having some questions about fixtures:
I made a fixture of my whole project db (not certain application) and I want to load it for each test, because it looks like loading only the fixture for certain app won't be enough.
I'd like to have the fixture stored in /proj_folder/fixtures/proj_fixture.json.
I've set the FIXTURE_DIRS = ('/fixtures/',) in my settings.py.
Then in my testcase I'm trying
fixtures = ['proj_fixture.json']
but my fixtures don't load.
How can this be solved?
How to add the place for searching fixtures?
In general, is it ok to load the fixture for the whole test_db for each test in each app (if it's quite small)?
Thanks!
I've specified path relative to project root in the TestCase like so:
from django.test import TestCase
class MyTestCase(TestCase):
fixtures = ['/myapp/fixtures/dump.json',]
...
and it worked without using FIXTURE_DIRS
Good practice is using PROJECT_ROOT variable in your settings.py:
import os.path
PROJECT_ROOT = os.path.dirname(os.path.realpath(__file__))
FIXTURE_DIRS = (os.path.join(PROJECT_ROOT, 'fixtures'),)
Do you really have a folder /fixtures/ on your hard disk?
You probably intended to use:
FIXTURE_DIRS = ('/path/to/proj_folder/fixtures/',)
Instead of creating fixures folder and placing fixtures in them (in every app), a better and neater way to handle this would be to put all fixtures in one folder at the project level and load them.
from django.core.management import call_command
class TestMachin(TestCase):
def setUp(self):
# Load fixtures
call_command('loaddata', 'fixtures/myfixture', verbosity=0)
Invoking call_command is equivalent to running :
manage.py loaddata /path/to/fixtures
Saying you have a project named hello_django with api app.
Following are steps to create fixtures for it:
Optional step: create fixture file from database: python manage.py dumpdata --format=json > api/fixtures/testdata.json
Create test directory: api/tests
Create empty file __init__.py in api/tests
Create test file: test_fixtures.py
from django.test import TestCase
class FixturesTestCase(TestCase):
fixtures = ['api/api/fixtures/testdata.json']
def test_it(self):
# implement your test here
Run the test to load fixtures into the database: python manage.py test api.tests
I did this and I didn't have to give a path reference, the fixture file name was enough for me.
class SomeTest(TestCase):
fixtures = ('myfixture.json',)
You have two options, depending on whether you have a fixture, or you have a set of Python code to populate the data.
For fixtures, use cls.fixtures, like shown in an answer to this question,
class MyTestCase(django.test.TestCase):
fixtures = ['/myapp/fixtures/dump.json',]
For Python, use cls.setUpTestData:
class MyTestCase(django.test.TestCase):
#classmethod
def setUpTestData(cls):
cls.create_fixture() # create_fixture is a custom function
setUpTestData is called by the TestCase.setUpClass.
You can use both, in which case fixtures is loaded first because setUpTestData is called after loading the fixtures.
You need to import from django.test import TestCase and NOT from unittest import TestCase. That fixed the problem for me.
If you have overridden setUpClass method, make sure you call super().setUpClass() method as the first line in the method. The code to load fixtures is in TestCase class.