I need to write some unit tests for an abstract base model, that provides some basic functionality that should be used by other apps. It it would be necessary to define a model that inherits from it just for testing purposes; are there any elegant/simple ways to define that model just for testing?
I have seen some "hacks" that make this possible, but never seen an "official" way in the django documentation or in other similar places.
Summary. There are two ways to unit test a class hierarchy and an abstract class: Using a test class per each production class. Using a test class per concrete production class.
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.
“Abstract base classes are useful when you want to put some common information into a number of other models. You write your base class and put abstract=True in the Meta class. This model will then not be used to create any database table.
The preferred way to write tests in Django is using the unittest module built-in to the Python standard library. This is covered in detail in the Writing and running tests document. You can also use any other Python test framework; Django provides an API and tools for that kind of integration.
Just stumbled across this feature myself: You can just inherit from your abstract model in tests.py and test that as usual. When you run 'manage.py tests', Django not only creates a test database, but also validates & syncs your test models.
Tested it with current Django trunk (version 1.2).
Updated for Django >=2.0
So I was running into a few problems using m4rk4l's answer: one being the 'RuntimeWarning: Model 'myapp.__test__mymodel' was already registered' issue brought up in one of the comments, another being tests failing because the table already exists.
I've added a few checks to help solve these issues and now it works flawlessly. I hope this helps people
from django.db import connection from django.db.models.base import ModelBase from django.db.utils import OperationalError from django.test import TestCase class AbstractModelMixinTestCase(TestCase): """ Base class for tests of model mixins/abstract models. To use, subclass and specify the mixin class variable. A model using the mixin will be made available in self.model """ @classmethod def setUpTestData(cls): # Create a dummy model which extends the mixin. A RuntimeWarning will # occur if the model is registered twice if not hasattr(cls, 'model'): cls.model = ModelBase( '__TestModel__' + cls.mixin.__name__, (cls.mixin,), {'__module__': cls.mixin.__module__} ) # Create the schema for our test model. If the table already exists, # will pass try: with connection.schema_editor() as schema_editor: schema_editor.create_model(cls.model) super(AbstractModelMixinTestCase, cls).setUpClass() except OperationalError: pass @classmethod def tearDownClass(self): # Delete the schema for the test model. If no table, will pass try: with connection.schema_editor() as schema_editor: schema_editor.delete_model(self.model) super(AbstractModelMixinTestCase, self).tearDownClass() except OperationalError: pass
To use, implement the same way as above (now with the correcting indentation):
class MyModelTestCase(AbstractModelMixinTestCase): """Test abstract model.""" mixin = MyModel def setUp(self): self.model.objects.create(pk=1) def test_a_thing(self): mod = self.model.objects.get(pk=1)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With