Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django: How to create a model dynamically just for testing

I have a Django app that requires a settings attribute in the form of:

RELATED_MODELS = ('appname1.modelname1.attribute1',                   'appname1.modelname2.attribute2',                    'appname2.modelname3.attribute3', ...) 

Then hooks their post_save signal to update some other fixed model depending on the attributeN defined.

I would like to test this behaviour and tests should work even if this app is the only one in the project (except for its own dependencies, no other wrapper app need to be installed). How can I create and attach/register/activate mock models just for the test database? (or is it possible at all?)

Solutions that allow me to use test fixtures would be great.

like image 678
muhuk Avatar asked Feb 02 '09 11:02

muhuk


People also ask

Can we create dynamic models in Django?

As of [5163], Django models can now be created dynamically at run-time, rather than being defined in a Python source file. While this may seem useless on the surface, it actually provides a powerful way to prototype a Django project, even by users who don't know a thing about Python!

What is def __ str __( self in Django?

def str(self): is a python method which is called when we use print/str to convert object into a string. It is predefined , however can be customised.

Should I test Django models?

If the code in question is built into Django, don't test it. Examples like the fields on a Model or testing how the built-in template. Node renders included tags. If your model has custom methods, you should test that, usually with unit tests.

What is automated testing in Django?

Automated Testing is a widely used tool that uses a collection of predefined test cases. Learn how Django and Selenium to ensure a bug free system. Table of contents.


2 Answers

You can put your tests in a tests/ subdirectory of the app (rather than a tests.py file), and include a tests/models.py with the test-only models.

Then provide a test-running script (example) that includes your tests/ "app" in INSTALLED_APPS. (This doesn't work when running app tests from a real project, which won't have the tests app in INSTALLED_APPS, but I rarely find it useful to run reusable app tests from a project, and Django 1.6+ doesn't by default.)

(NOTE: The alternative dynamic method described below only works in Django 1.1+ if your test case subclasses TransactionTestCase - which slows down your tests significantly - and no longer works at all in Django 1.7+. It's left here only for historical interest; don't use it.)

At the beginning of your tests (i.e. in a setUp method, or at the beginning of a set of doctests), you can dynamically add "myapp.tests" to the INSTALLED_APPS setting, and then do this:

from django.core.management import call_command from django.db.models import loading loading.cache.loaded = False call_command('syncdb', verbosity=0) 

Then at the end of your tests, you should clean up by restoring the old version of INSTALLED_APPS and clearing the app cache again.

This class encapsulates the pattern so it doesn't clutter up your test code quite as much.

like image 197
Carl Meyer Avatar answered Sep 20 '22 18:09

Carl Meyer


@paluh's answer requires adding unwanted code to a non-test file and in my experience, @carl's solution does not work with django.test.TestCase which is needed to use fixtures. If you want to use django.test.TestCase, you need to make sure you call syncdb before the fixtures get loaded. This requires overriding the _pre_setup method (putting the code in the setUp method is not sufficient). I use my own version of TestCase that lets me add apps with test models. It is defined as follows:

from django.conf import settings from django.core.management import call_command from django.db.models import loading from django import test  class TestCase(test.TestCase):     apps = ()      def _pre_setup(self):         # Add the models to the db.         self._original_installed_apps = list(settings.INSTALLED_APPS)         for app in self.apps:             settings.INSTALLED_APPS.append(app)         loading.cache.loaded = False         call_command('syncdb', interactive=False, verbosity=0)         # Call the original method that does the fixtures etc.         super(TestCase, self)._pre_setup()      def _post_teardown(self):         # Call the original method.         super(TestCase, self)._post_teardown()         # Restore the settings.         settings.INSTALLED_APPS = self._original_installed_apps         loading.cache.loaded = False 
like image 32
Conley Owens Avatar answered Sep 16 '22 18:09

Conley Owens