Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django: Best way to unit-test an abstract model

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.

like image 478
Bernhard Vallant Avatar asked Nov 26 '10 00:11

Bernhard Vallant


People also ask

Can you unit test an abstract class?

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.

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.

Why do we use abstract models in Django?

“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.

How do I test a Django project?

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.


2 Answers

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).

like image 52
maikhoepfel Avatar answered Sep 28 '22 10:09

maikhoepfel


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) 
like image 28
Dustin Gault Avatar answered Sep 28 '22 11:09

Dustin Gault