【问题标题】:How to apply Test Driven Development with Django Models?如何使用 Django 模型应用测试驱动开发?
【发布时间】:2014-07-10 10:48:43
【问题描述】:

我最近了解了测试驱动开发,并想在我的 Django 项目中开发新应用时试一试。我一直在阅读Test-Driven Development with Python,这很棒。但是,我有时会发现书中的示例(待办事项列表)过于简单——例如,当testing Models is introduced 时,作者有一个创建对象的测试,保存它们,然后从数据库来检查它们的值。当然,当您的模型只有一个 ModelField 时,这很容易。

但是当你的模型有 20 个 ModelField 时呢?您是否应该有一个测试来创建一个对象及其所有字段,然后保存该对象,然后检查每个字段的值?对每个领域进行单独测试会更好吗?

在我的具体情况下,我有一个模型,其中包含大约五个必填字段,然后还有大约十五个可选字段。我现在的想法是首先在我的 TestCase 类中创建一个函数,该函数使用默认字段创建此模型的对象。然后,我将进行测试以确保该对象正常保存,然后对每个单独的可选字段进行另一次测试。似乎有很多测试,但不是很多小测试比一个大测试好吗?

洞察力赞赏!

【问题讨论】:

    标签: django unit-testing testing tdd django-testing


    【解决方案1】:

    这就是模型工厂大有帮助的地方。有两个流行的模块提供它:

    我个人用过factory_boy,觉得非常好用。

    基本上,您使用默认字段值定义工厂:

    class UserFactory(factory.Factory):
        class Meta:
            model = models.User
    
        first_name = 'John'
        last_name = 'Doe'
        admin = False
    

    然后,您可以use the factory 并在需要时覆盖字段值。它还支持SequencesLazy Attributes和其他有用的数据生成功能。

    谈到你的特定任务,尽量不要测试 django 实际测试的内容。例如,无需测试required 参数是否有效。测试您在操作模型时所涉及的自定义模型逻辑。

    另外,A Guide to Testing in Django 也是一本好书。

    希望对您有所帮助。

    【讨论】:

    • 我明白你在说什么,但是在具体谈到测试驱动的开发方法时,我认为我不应该在没有测试之前编写代码,对吧?所以如果我想在我的模型上有一个 ModelField,我必须对此进行测试,不是吗?不是我想测试required 是否有效,而是我想测试该字段本身是否适用于我的模型。即,如果我创建一个对象并设置该字段并保存它,它会回来吗?是的,这在某种程度上测试了 Django,但它也为我的模型本身提供了设计和测试。
    • @Joseph 正确,严格来说,如果您想完全遵循 TDD 方法,那么是的,在模型上添加新字段之前,您需要为它编写一个测试,看到它失败,添加字段并查看测试通过。但是,就我个人而言,我通常不会那么深入。时间很宝贵,通常有更重要的事情需要测试。这只是我的观点。我真的很喜欢测试。
    • 谢谢,我想现在我不会走那么远,就像你提到的那样。我听说 TDD 需要喝一些清凉饮料,所以我想诚实地尝试一下,即使其中包括看起来他们会做更多工作或浪费时间的事情——理想情况下,回报是以后节省时间和工作。感谢您的帮助,感谢您提供指向 Django 测试指南的链接。
    • @Joseph 太好了,谢谢。我还建议在 Testing and Django 上观看 Carl Meyer 的演讲。此外,djangotesting 上还有 other great videos。如果您需要任何进一步的帮助,请告诉我。祝测试愉快!
    【解决方案2】:

    您为什么要测试将值保存和加载到数据库的情况?那是姜戈的责任。 Django 有一整套测试来详细检查其数据库层是否正常工作。绝对没有理由检查任何字段的基本行为,更不用说所有字段了。

    您的单元测试适用于您的逻辑:您的自定义方法、您的视图、您的模板标签。它们不适用于 Django 默认提供的逻辑。

    【讨论】:

    • 正如另一个答案中提到的,我特别指的是测试驱动开发,而不是对我的 Django 应用程序的“常规”测试。就像@alecxe 所证实的那样,真正遵循 TDD 方法需要在代码之前进行测试,即使对于像 ModelField 这样简单的东西也是如此。
    • 与其说是测试保存和加载值,不如说是您在这些字段上选择了正确的字段和属性。例如,如果您的模型中有一个名为 account_number 的字段必须是唯一的,则应该进行测试以确保您已编写模型以在该字段上正确添加 unique=True 属性,因为这是一项业务应由测试强制执行的要求。
    【解决方案3】:

    我是这本书的作者。我的意思是该测试更多地是对 Django ORM 的介绍,而不是作为最佳实践的演示,我当时试图解释这一点,但我认为一些混乱是不可避免的。我会考虑如何以不同的方式呈现事物。

    无论如何,如果您跳到本书中的a few chapters later,我将展示如何将测试简化为最佳实践。

    您是否测试基本的 Django 模型取决于您——有些人会说测试声明性语法是多余的,其他人会说简短的测试可以作为占位符。这是您可能会使用的一个:

    class Book(models.Model):
        title = models.CharField(max_length=200)
        author = models.ForeignKey(Author)
        ISBN = models.CharField(max_length=35)
        abstract = models.TextField()
    
    
    class BookTest(TestCase):
    
        def test_defaults(self):
            book = Book()
            self.assertEqual(book.title, '')
            self.assertEqual(book.author, None)
            self.assertEqual(book.ISBN, '')
            self.assertEqual(book.abstract, '')
    

    所以这是一个占位符。如果您开始引入更复杂的字段,例如默认值为datetime.today() + one_monthpublication_date 字段,它鼓励您添加更多测试,这可能需要进行一些测试以确保您正确。使用占位符可以降低后续测试的障碍。其他人会告诉你这太过分了。你必须找到自己的平衡点。

    相当广泛接受的一件事是您绝对应该测试行为。所以,如果你的模型有自定义方法:

    class Book(models.Model):
        # [...]
    
        def is_available(self):
            return self.pub_date < datetime.today() and Stock.objects.filter(book=self).count() > 0
    

    那么对此进行某种测试绝对是个好主意。

    【讨论】:

    • 尚未阅读您的书,但看过您的 2 篇 TDD 教程。 Oreilly TDD 网络广播和 2013 Pycon(我去过)。我准备观看更多关于 TDD 的演讲。我不得不说,你发布了这么多关于 TDD 的教程,是非常公正的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-27
    • 2011-02-11
    • 1970-01-01
    相关资源
    最近更新 更多