A Django project I’m working on defines two databases in its config: The standard/default internal db as well as a remote legacy read-only database belonging to my organization. Models for the read-only db were generated by inspectdb
, and naturally have managed = False
in their Meta class, which prevents Django from attempting any form of migration on them.
Unfortunately, that also prevents the Django test runner from trying to create a schema mirror of it during test runs. But what if you want to stub out some sample data from the read-only database into a fixture that can be loaded and accessed during unit tests? You’ll need to do the following:
- Tell Django to create the second test database locally rather than on the remote host
- Disable any routers you have that route queries for certain models through the remote db
- Tell Django to override the
Managed = False
attribute in the Meta class during the test run
Putting that all together turned out to be a bit tricky, but it’s not bad once you understand how and why you need to take these steps. Because you’ll need to override a few settings during test runs only, it makes sense to create a separate test_settings.py
to keep everything together:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | from project.local_settings import * from django.test.runner import DiscoverRunner class UnManagedModelTestRunner(DiscoverRunner): ''' Test runner that automatically makes all unmanaged models in your Django project managed for the duration of the test run. Many thanks to the Caktus Group: http://bit.ly/1N8TcHW ''' def setup_test_environment( self , * args, * * kwargs): from django.db.models.loading import get_models self .unmanaged_models = [m for m in get_models() if not m._meta.managed] for m in self .unmanaged_models: m._meta.managed = True super (UnManagedModelTestRunner, self ).setup_test_environment( * args, * * kwargs) def teardown_test_environment( self , * args, * * kwargs): super (UnManagedModelTestRunner, self ).teardown_test_environment( * args, * * kwargs) # reset unmanaged models for m in self .unmanaged_models: m._meta.managed = False # Since we can't create a test db on the read-only host, and we # want our test dbs created with postgres rather than the default, override # some of the global db settings, only to be in effect when "test" is present # in the command line arguments: if 'test' in sys.argv or 'test_coverage' in sys.argv: # Covers regular testing and django-coverage DATABASES[ 'default' ][ 'ENGINE' ] = 'django.db.backends.postgresql_psycopg2' DATABASES[ 'default' ][ 'HOST' ] = '127.0.0.1' DATABASES[ 'default' ][ 'USER' ] = 'username' DATABASES[ 'default' ][ 'PASSWORD' ] = 'secret' DATABASES[ 'tmi' ][ 'ENGINE' ] = 'django.db.backends.postgresql_psycopg2' DATABASES[ 'tmi' ][ 'HOST' ] = '127.0.0.1' DATABASES[ 'tmi' ][ 'USER' ] = 'username' DATABASES[ 'tmi' ][ 'PASSWORD' ] = 'secret' # The custom routers we're using to route certain ORM queries # to the remote host conflict with our overridden db settings. # Set DATABASE_ROUTERS to an empty list to return to the defaults # during the test run. DATABASE_ROUTERS = [] # Set Django's test runner to the custom class defined above TEST_RUNNER = 'project.test_settings.UnManagedModelTestRunner' |
With that in place, you can now run your tests with:
./manage.py test --settings=project.test_settings
… leaving settings untouched during normal site operations. You can now serialize some data from your read-only host and load it as a fixture in your tests:
1 2 3 4 5 6 7 8 9 10 11 | class DirappTests(TestCase): # Load test data into both dbs: fixtures = [ 'auth_group.json' , 'sample_people.json' ] ... def test_stub_data( self ): # Guarantees that our sample data is being loaded in the test suite person = Foo.objects.get( id = 7000533 ) self .assertEqual(person.first_name, "Quillen" ) |