【问题标题】:How to load fixtures only once in django unit tests ?如何在 django 单元测试中只加载一次固定装置?
【发布时间】:2010-11-02 00:58:41
【问题描述】:

在单元测试中我需要加载fixtures,如下:

   class TestQuestionBankViews(TestCase):

        # Load fixtures
        fixtures = ['qbank']

        def setUp(self):                           
            login = self.client.login(email="mail@gmail.com",password="welcome")        


        def test_starting_an_exam_view(self):               
            candidate = Candidate.objects.get(email="mail@gmail.com")
            .......etc


        def test_review_view(self):
            self.assertTrue(True)            
            .........

       def test_review_view2(self):
            self.assertTrue(True)
            .........

问题:

每次测试都会加载这些固定装置,即在 test_review_viewtest_review_view2 等之前,因为 Django 在每次测试后都会刷新数据库。

此行为导致测试需要很长时间才能完成。

如何防止这种多余的夹具加载?

有没有办法在 setUp 中加载夹具并在测试类完成时刷新它们,而不是在每个测试之间刷新?

【问题讨论】:

  • 哦............我想我可以通过使用 intial_data 夹具并继承“unittest.Testcase”而不是“test.TestCase”来解决这个问题?还有其他想法吗?

标签: django unit-testing


【解决方案1】:

使用django-nose 和一些代码,您可以完全按照您的要求进行操作。使用 django-nose,您可以拥有每个包、每个模块和每个类的设置和拆卸功能。这允许您在更高的设置功能之一中加载您的夹具,并禁用 django.test.TestCase 在测试之间重置夹具。

这是一个示例测试文件:

from django.test import TestCase
from django.core import management

    def setup():
        management.call_command('loaddata', 'MyFixture.json', verbosity=0)

    def teardown():
        management.call_command('flush', verbosity=0, interactive=False)

    class MyTestCase(TestCase):

        def _fixture_setup(self):
            pass

        def test_something(self):
            self.assertEqual(1, 1)

请注意,设置和拆卸不在课程范围内。 setup 将在此文件中的所有测试类之前运行,而 teardown 将在所有测试类之后运行。

在类中,您会注意到 def _fixture_setup(self) 方法。这会覆盖在每次测试之间重置数据库的函数。

请记住,如果您的测试将任何内容写入数据库,这可能会使您的测试无效。因此,任何其他需要为每个测试重新加载夹具的测试都应该放在不同的测试文件中。

【讨论】:

  • +1 用于功能性解决方案,实际上为与使用 initial_data.json/yaml 不同的方法留出了空间。 (我使用自定义管理命令和调用 Django ORM 的脚本来创建我的初始数据。这让我可以完美地整合它。)这应该是公认的答案,它帮助了我很多!
  • 很好的解决方案!我发现的一件事非常有用。如果您不输入 def _fixture_setup(self) 方法,并允许 Django TestCase 像往常一样拆除,那么在一个测试用例中所做的任何数据库更改都不会影响下一个测试用例。这是因为在测试用例开始时,会启动一个数据库事务,在拆卸阶段,测试用例将执行数据库回滚以重置数据库的状态。所以忽略 def _fixture_setup() 会给你两全其美的赌注!
【解决方案2】:

或者使用setUpModule:

def setUpModule():
    print 'Module setup...'

def tearDownModule():
    print 'Module teardown...'

class Test(unittest.TestCase):
    def setUp(self):
       print 'Class setup...'

    def tearDown(self):
       print 'Class teardown...'

    def test_one(self):
        print 'One'

    def test_two(self):
        print 'Two'

打印:

Creating test database for alias 'default'...
Module setup...
Class setup...
One
Class teardown...
Class setup...
Two
Class teardown...
Module teardown...

【讨论】:

  • 这只有在模块上的每个测试都使用相同的夹具时才有效。换句话说,它迫使您通过他们使用的夹具对给定模块进行测试,而不是他们测试的内容。
【解决方案3】:

对于它的价值,并且由于没有公认的答案,Django 1.8 现在提供了开箱即用的功能 - 只要您使用支持事务的数据库后端。

它还添加了 TestCase.setUpTestData() 方法,用于为每个 TestCase 类手动创建一次测试数据。

the Django 1.8 release notes

【讨论】:

    【解决方案4】:

    如果您不想为此目的安装新软件包,您可以结合使用 Tom Wainwright's solutionmhost's solution

    在您的测试文件中,将这些函数添加到任何类之外:

    from django.core.management import call_command
    
    def setUpModule():
        call_command(
            'loaddata', 
            'path_to_fixture.json',
            verbosity=0
        )
    
    def tearDownModule():
        call_command('flush', interactive=False, verbosity=0)
    

    如果您不想将这些夹具加载到所有测试用例的数据库中,split the test into multiple files 通过在应用程序中创建一个名为 tests 的新目录,添加一个空的 __init__.py 文件来告诉 Python 这是一个包,并添加文件名以test 开头的测试文件,因为运行程序将查找与模式test*.py 匹配的文件

    【讨论】:

    • 注意,如果使用这种方法,您需要同时覆盖 _fixture_setup 和 _fixture_teardown 以便您在 setupmodule 中加载的夹具不会被夹具拆卸丢弃。
    • 另请注意,这种方法不会重置自动递增的主键/ID;因此,您不应指望在测试中从一个开始(或重新启动)的 ID。
    【解决方案5】:

    我遇到了同样的问题。一般来说,使用 django 的测试运行器并没有真正好的方法来做到这一点。你可能对此感兴趣thread

    话虽如此,如果所有测试用例都使用相同的夹具,并且它们不以任何方式修改数据,那么使用 initial_data 就可以了。

    【讨论】:

    • 感谢您的指点,& 是的(我之前没有考虑过)只有在每个测试用例不修改数据库的情况下,我们才能使用 intial_data
    • 我决定将 unittest.TestCase 与 intial_data 一起使用。关于如何获得 django.test.TestCase 提供的所有便利的任何想法?
    【解决方案6】:

    我曾经遇到过类似的问题,最终编写了自己的测试运行程序。在我的情况下,initial_data 不是正确的位置,因为 initial_data 将在 syncdb 期间加载,这是我不想要的。我覆盖了 setup_teardown_test_environment 方法以在测试套件运行之前加载我的自定义夹具,并在完成后将其删除。

    【讨论】:

      【解决方案7】:

      django-nose 为这个问题提供了现成的解决方案:简单地继承django_nose.FastFixtureTestCase

      此外,django-nose 支持夹具捆绑,它可以通过在每次测试运行时仅加载每组独特的夹具一次来进一步加快您的测试运行。在适当的地方对FastFixtureTestCase 进行子类化后,使用--with-fixture-bundling 选项运行django-nose 测试运行程序。

      更多信息请参见django-nose on pypi

      【讨论】:

        猜你喜欢
        • 2011-01-29
        • 1970-01-01
        • 1970-01-01
        • 2016-04-01
        • 2011-09-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-10
        相关资源
        最近更新 更多