Django:对抽象模型进行单元测试的最佳方法

Ber*_*ant 22 django unit-testing django-models abstract

我需要为抽象基础模型编写一些单元测试,它提供了其他应用程序应该使用的一些基本功能.为了测试目的,有必要定义一个继承它的模型; 是否有任何优雅/简单的方法来定义该模型仅用于测试

我看到一些"黑客"使这成为可能,但从未在django文档或其他类似的地方看到过"官方"方式.

小智 17

我自己偶然发现了这个功能:您可以在tests.py中继承您的抽象模型并像往常一样进行测试.当您运行'manage.py tests'时,Django不仅会创建测试数据库,还会验证和同步您的测试模型.

用当前的Django trunk(版本1.2)测试它.

  • 只需添加一件事:如果你的测试是在一个文件夹中,而不仅仅是在tests.py中(而且我的测试永远不适合一个文件),那么你需要拥有Meta内部类,并设置你的app_label(只是就像你拆分了models.py文件一样). (6认同)
  • 听起来不错......但在我的情况下它不起作用.将继承的类放在models.py中可以正常工作,但是将该类放在tests.py中将不会获得"syncdb for tests"创建表.要明确:我只想要这个表进行测试.使用Django 1.2.3.有任何想法吗?注意:使用django-nose测试跑步者.也许它的行为不同(现在正在研究). (2认同)
  • 更新:确实在 django-nose 运行程序中发生错误,但使用标准 django 测试运行程序可以正常工作。 (2认同)
  • 这似乎已停止在Django 1.5中工作? (2认同)
  • 使用迁移时不会使用Django 1.7 (2认同)
  • 它引发错误“django.db.utils.OperationalError:没有这样的表:myapp_childclass”(这是有道理的,因为继承抽象类的类尚未经过 makemigrations。 (2认同)

小智 10

我也有同样的情况.我最终使用了@dylanboxalot解决方案的版本.在阅读"测试结构概述"部分后,请从此处获得更多详细信息.

setUptearDown每次测试运行时间的方法被调用.更好的解决方案是在运行所有测试之前运行一次"抽象"模型的创建.为此,您可以实现setUpClassData并实现tearDownClass.

class ModelMixinTestCase(TestCase):
    '''
    Base class for tests of model mixins. To use, subclass and specify the
    mixin class variable. A model using the mixin will be made available in
    self.model
    '''
    @classmethod
    def setUpClass(cls):
        # Create a dummy model which extends the mixin
        cls.model = ModelBase('__TestModel__' +
            cls.mixin.__name__, (cls.mixin,),
            {'__module__': cls.mixin.__module__}
        )

        # Create the schema for  our test model
        with connection.schema_editor() as schema_editor:
            schema_editor.create_model(cls.model)
        super(ModelMixinTestCase, cls).setUpClass()

    @classmethod
    def tearDownClass(cls):
        # Delete the schema for the test model
        with connection.schema_editor() as schema_editor:
            schema_editor.delete_model(cls.model)
        super(ModelMixinTestCase, cls).tearDownClass()
Run Code Online (Sandbox Code Playgroud)

可能的实现可能如下所示:

class MyModelTestCase(ModelMixinTestCase):
    mixin = MyModel

    def setUp(self):
        # Runs every time a test is run.
        self.model.objects.create(pk=1)

    def test_my_unit(self):
        # a test
        aModel = self.objects.get(pk=1)
        ...
Run Code Online (Sandbox Code Playgroud)

也许ModelMixinTestCase类应该添加到Django?:P


小智 8

我最近偶然发现了这个并希望为更新的Django版本(1.9及更高版本)更新它你可以使用SchemaEditor create_model而不是过时的sql_create_model

from django.db import connection
from django.db.models.base import ModelBase
from django.test import TestCase


class ModelMixinTestCase(TestCase):
    """
    Base class for tests of model mixins. To use, subclass and specify
    the mixin class variable. A model using the mixin will be made
    available in self.model.
    """

    def setUp(self):
        # Create a dummy model which extends the mixin
        self.model = ModelBase('__TestModel__' + self.mixin.__name__, (self.mixin,), {'__module__': self.mixin.__module__})

        # Create the schema for our test model
        with connection.schema_editor() as schema_editor:
            schema_editor.create_model(self.model)

    def tearDown(self):
        # Delete the schema for the test model
        with connection.schema_editor() as schema_editor:
            schema_editor.delete_model(self.model)
Run Code Online (Sandbox Code Playgroud)

  • 我正在运行`django / db / models / base.py:325:RuntimeWarning:当我的测试类中的第二个测试方法运行时,模型'myapp .__ test__mymodel'已被注册'。tearDown方法不应该防止这种情况吗? (2认同)

DSy*_*rgy 8

更新了Django> = 2.0

所以我使用m4rk4l的答案遇到了一些问题:一个是'RuntimeWarning:Model'myapp .__ test__mymodel'已经注册'在其中一个评论中出现的问题,另一个是测试失败,因为该表已经存在.

我添加了一些检查来帮助解决这些问题,现在它可以完美运行.我希望这有助于人们

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
Run Code Online (Sandbox Code Playgroud)

要使用,请执行与上面相同的方法(现在使用更正缩进):

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)
Run Code Online (Sandbox Code Playgroud)

  • 所以数据库实际上在这里有所不同: Mysql: OperationalError Postgresql: PlanningError (2认同)

sim*_*lmx 7

我认为你要找的是这样的.

这是链接的完整代码:

from django.test import TestCase
from django.db import connection
from django.core.management.color import no_style
from django.db.models.base import ModelBase

class ModelMixinTestCase(TestCase):                                         
    """                                                                     
    Base class for tests of model mixins. To use, subclass and specify      
    the mixin class variable. A model using the mixin will be made          
    available in self.model.                                                
    """                                                                     

    def setUp(self):                                                        
        # Create a dummy model which extends the mixin                      
        self.model = ModelBase('__TestModel__'+self.mixin.__name__, (self.mixin,),
            {'__module__': self.mixin.__module__})                          

        # Create the schema for our test model                              
        self._style = no_style()                                            
        sql, _ = connection.creation.sql_create_model(self.model, self._style)

        self._cursor = connection.cursor()                                  
        for statement in sql:                                               
            self._cursor.execute(statement)                                 

    def tearDown(self):                                                     
        # Delete the schema for the test model                              
        sql = connection.creation.sql_destroy_model(self.model, (), self._style)
        for statement in sql:                                               
            self._cursor.execute(statement)                                 
Run Code Online (Sandbox Code Playgroud)


小智 3

开发一个与“抽象”模型一起分发的最小示例应用程序。为示例应用程序提供测试以证明抽象模型。