Overriding TEST_RUNNER with #override_settings - django

In order to run my tests faster I created a no db test runner as in this answer. Then I needed to set the TEST_RUNNER settings variable to my new test runner but only for certain tests. To achieve this, I tried using django.test.utils.override_settings decorator like this (as in the docs):
from django.test import TestCase
from django.test.utils import override_settings
class MyTestCase(TestCase):
#override_settings(TEST_RUNNER='path_to_my_no_db_test_runner')
def test_my_test_case(self):
...
The problem is that when I run this test, django will still create the database, which is not the expected behavior of course. The curious thing is that if I set the TEST_RUNNER directly in my settings.py it works perfectly, but with django.test.utils.override_settings it seems to have no effect. I also tried using this override_settings module but got the same results.
What am I dping wrong? Is there any other way of achieving this? I'd rather not to create a test_settings.py and run my tests with --settings argument.

Put this piece of code in your config :
TESTING = 'test' in sys.argv
...
if TESTING:
TEST_RUNNER = 'path_to_my_no_db_test_runner'
DATABASES = {}
The TESTING setting will be defined only when you run tests, you can then dynamically change some settings including your DB, migrations, test runners...
It will be loaded at the very beginning of Django initialisation therefore if you override DATABASES no DBs will be created.

Related

How to load fixtures for a LiveServerTestCase

I am attempting to write test cases in Django using Selenium. I want to use existent fixtures so my test database (SQLite3) has test data for every test.
I have some model test cases (just using the TestCase class) as follows;
from django.test import TestCase
from django.test import LiveServerTestCases
from missions.models import Mission, MissionDataRecord
class MissionModelTests(TestCase):
fixtures = ['myproject_common/fixtures/auth_initial_load.json', 'worklog/fixtures/worklogs_initial_load',
'missions_initial_load.json']
def test_object_name_is_mission_title(self):
mission = Mission.objects.get(id=1)
self.assertEqual(mission.title, str(mission))
def test_object_name_is_mission_title_again(self):
mission = Mission.objects.get(id=1)
self.assertEqual(mission.title, str(mission))
This works as expected when run like this (I get two test passes). However, for Selenium testing I need to use LiveServerTestCase instead of TestCase.
The simple example above is a model test, but for illustrative purposes of the issue I'm facing with Selenium, if I simply replace "TestCase" with "LiveServerTestCase" the first test passes, but the second test fails, with the error
django.db.utils.IntegrityError: Problem installing fixture
'[...]/fixtures/auth_initial_load.json': Could not load
auth.User(pk=1): UNIQUE constraint failed: auth_user.username
This error occurs in the _fixture_setup of /django/test/testcases.py. This seems to suggests that my fixtures (specifically the auth_initial_load fixture) is attempting to load again ON TOP of existing data. However, from django docs reading this should not be taking place, because each test should run in its own transaction (which I believe means the fixtures are loaded for each transaction).
What is going on here, and more importantly, how can I use LiveServerTestCase with my existing fixtures (similar to how I am using TestCase currently)? In reality I need to use StaticLiveServerTestCase, but Im guessing the code will be the same.
It turns out the way I was loading fixtures correctly after all. The issue was with my fixtures themselves, using hard coded primary (and foreign) keys. In my case two users were being created before the fixtures were loaded, so when fixtures tried to load with the same primary key, a UNIQUE constraint violation occurred. The solution was to re-generate my fixtures using the --natural-primary and --natural-foreign flags as suggested in this SO answer.

Testing backend code in Django

I am writing an authentication back-end in Django to log only a few users.
It is in a folder called restrictedauthentification/ which is at the root of my Django Project. (I am written it down for a specific project.)
It has two files in it : backend.py and tests.py
In the last file, I have written down some tests for it.
But I can't run them with command ./manage.py test because it isn't an installed app.
Any ideas how I could run them ?
Okay, I found a solution that keep me from turning my backend into a module.
Somthing that I didn't understand and that could help some beginners : In python, a test cannot run itself. It need to be executed by a TestRunner.
Now, one could use the TextTestRunner bundled python that execute the tests and show the results on the standard output, but when testing with django, one need to do one thing before and after the test: calling the function setup_test_environment() and teardown_test_environment().
So I just created a class that inherit from TextTestRunner and redefine its methode run() in order that it execute the two functions provided by Django.
Here it is :
from restrictedauthentification.tests import TestRestrictedAuthentification
from django.test.utils import setup_test_environment, teardown_test_environment
from unittest import TextTestRunner
class DeadSimpleDjangoTestRunner(TextTestRunner):
def run(self, test):
setup_test_environment()
super().run(test)
teardown_test_environment()

run every TestCase inside a module

How can you run tests from all TestCase classes, in a specific module under tests package?
In a Django project, I have split tests.py under tests/
Each file(module) has several TestCase classes, and each of them having several test methods.
init.py imports each of them.
I already know that I can do these:
Run all the test:
./manage.py test myapp
Or run specific TestCase:
./manage.py test myapp.OneOfManyTestCase
Or run very specific test method from a TestCase class:
./manage.py test myapp.OneOfManyTestCase.test_some_small_method
However, I can't figure out how to run every TestCases from a particular module.
Say, OneOfManyTestCase class is from tests/lot_of_test.py, and there are other test cases too.
Django doesn't seem to care about modules with TestCases.
How can I run all the TestCases inside lot_of_test?
I think to achieve this you need to subclass your own TestRunner from DjangoTestSuiteRunner and override build_suite method.
I ended up writing down my own TestSuiteRunner, like #sneawo said.
After Django-style fails, try importing as usual python-style.
One line to fix:
suite.addTest(build_test(label))
into
try:
suite.addTest(django.test.simple.build_test(label))
except ValueError:
# change to python-style package name
head, tail = label.split('.', 1)
full_label = '.'.join([head, django.test.simple.TEST_MODULE, tail])
# load tests
tests = unittest.defaultTestLoader.loadTestsFromName(full_label)
suite.addTests(tests)
and set TEST_RUNNER in settings.py:
TEST_RUNNER='myapp.tests.module_test_suite_runner.ModuleTestSuiteRunner'

Django Unit Test fails project-wide but passes app-wide

I'm using Django 1.4 with Python 2.7 on Ubuntu 12.10.
I have a project with several apps and dozens of unit tests. We've recently run into a little issue using the #override_settings decorator.
Here is some code:
#override_settings(MEDIA_URL='/test/media/')
def test_get_content_dict(self):
self.assertEqual(
self.offer.get_content_dict(),
{ 'some stuff': some stuff }
When tests are run at the app level everything passes.
python manage.py test my_app --settings=proton.settings.test
But when we run at the project level it fails.
python manage.py test --settings=proton.settings.test
It's failing due to some stuff using /test/media but the model method offer.get_contect_dict() using /media, which is our actual MEDIA_URL.
We can change the MEDIA_URL in our settings/test.py file, but that would require all tests to use /test/media (which might be a good idea anyway).
Clearly the problem is in Django's core.files.storage.FileSystemStorage.__init__() - it sets the base_url earlier in the test suite but does not re-instantiate the object after each test (for obvious reasons) so the #override_settings doesn't actually do anything.
Is this a bug or is this working as intended? Any suggestions to an elegant solution other than forcing all unit tests to use /test/media by setting our MEDIA_URL constant in our settings/test.py to /test/media?

Django ignoring fixtures when run from tests

I have a simple class in a Django app called "project"
from django.test import TestCase
class ProjectTest(TestCase):
"""Unit tests for the "Project" app """
fixtures = ['test_data.json', ]
def setUp(self):
pass
def testTotalAmountOfWhuffie(self):
"""Tests that the calculation to find the total amount of Whuffie allocated is correct."""
pass
if __name__ == '__main__':
unittest.main()
and when I run
manage.py test project
it completely ignores the fixtures. If I rename the file to initial_data.json it gets picked up by the test runner, so I'm sure the directory structure is right.
If I increase the verbosity of the test runs, they don't even look for the fixtures that I specify, I can even go as far as specifying the full path to the file, and they don't get loaded.
If 'test_data.json' is under project/fixtures, then I can't see any problem with what you have got. Try using the fixture data in a test.
"it completely ignores the fixtures"
How do you know this?
"If I rename the file to initial_data.json it gets picked up by the test runner"
This is only picked up because the test runner runs syncdb. See http://docs.djangoproject.com/en/dev/howto/initial-data/#automatically-loading-initial-data-fixtures
"If I increase the verbosity of the test runs, they don't even look for the fixtures that I specify,"
How can you be sure? I ran my tests with the verbosity set to 3, and the test runner did not mention my fixtures at all. (The tests work)
I even tried using a non-existent fixture in the test. Again, the test runner did not mention any problem.