今回はDjangoのAbstract Modelを直接テストする方法を解説する。
次のようなモデル定義を想定とする。 Postがモデルとなるが、その基底クラスとしてPostBaseを定義している。 今回はこの基底クラスを単体でテストしたいとする。
blog/models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth import get_user_model
UserModel = get_user_model()
class PostBase(models.Model):
class Meta:
abstract = True
user = models.ForeignKey(UserModel, on_delete=models.CASCADE)
published = models.BooleanField(blank=True, null=True, default=False)
def mark_publish(self):
self.published = True
class Post(PostBase):
title = models.CharField(max_length=200)
Abstract Model自体はテーブルを持たないため、単体ではDBの操作を行えない。 そこでテストクラス内で一時的なモデルを作成する。 そのために以下の関数を準備した。 create_test_model()は一時的なモデルを生成する。 register_test_model()は生成した一時的なモデルをDjangoに登録する。 unregister_test_model()はDjangoに登録した一時的なモデルの登録を削除する。
blog/tests/testing.py
from django.db import connection
from django.db.models.base import ModelBase
def create_test_model(abstract_model_class):
return ModelBase(
"__TestModel__" + abstract_model_class.__name__,
(abstract_model_class,),
{"__module__": abstract_model_class.__module__},
)
def register_test_model(model):
with connection.schema_editor() as schema_editor:
schema_editor.create_model(model)
def unregister_test_model(model):
"""
登録したテスト用のモデルを削除するために作成したがForeignKeyなどの
リレーションがある場合リレーション先のモデルのメタ情報にフィールド
が残ってしまいレコードの削除に失敗するようになった。
"""
with connection.schema_editor() as schema_editor:
# del model._meta.apps.app_configs["blog"].models["__testmodel__postbase"]
# schema_editor.remove_field(model, field)
for field in model._meta.get_fields():
schema_editor.remove_field(model, field)
schema_editor.delete_model(model)
pass
前述の関数を以下のようにテストの実行前に呼び出すようにテストを実装する。
blog/tests/test_models.py
from blog.models import PostBase
from django.contrib.auth import get_user_model
from django.test import TestCase
from .testing import create_test_model, register_test_model
UserModel = get_user_model()
class PostBaseTest(TestCase):
@classmethod
def setUpClass(cls):
cls.model = create_test_model(PostBase)
register_test_model(cls.model)
@classmethod
def tearDownClass(cls):
pass
def test_it(self):
user = UserModel.objects.create()
obj = self.model.objects.create(user=user)
obj.mark_publish()
self.assertTrue(obj.published)
テストを実行するとテストはパスする。
以下、テストで作成したアプリケーションのファイル構成を貼っておく。