【问题标题】:How to test Models in Django with Foreign Keys如何使用外键在 Django 中测试模型
【发布时间】:2010-09-11 06:09:03
【问题描述】:

我想确保我是单独测试模型/对象,而不是作为一个庞大的系统。

如果我有一个 Order 对象,并且它具有客户、付款、OrderItems 等的外键,并且我想测试 Order 功能,我需要为所有相关数据创建固定装置,或者在代码中创建它。我认为我真正需要做的是模拟其他项目,但如果我对这些外键进行查询,我看不到一个简单(或可能)的解决方案。

常见的解决方案(夹具)并不能真正让我一次测试一个对象。我确信这部分是由于我的应用方式过度耦合造成的。

我正在尽最大努力采用 TDD 作为我的主要工作方法,但使用 Django 的工作方式,您似乎可以运行非常琐碎的单元测试,或者这些大规模的集成测试。

[编辑]更好的明确示例和更谦逊的态度

我的意思是 我似乎 只能运行微不足道的单元测试。我见过人们使用经过良好测试和细化的模块。我确信其中一些可以追溯到糟糕的设计。

例子:

我有一个名为 Upsell 的模型,它链接到 Product 模型。然后我有一个 Choices 模型,它是 Upsell 的子代(你想要 #1、#2、#3 门后面的东西吗)。

Upsell 模型上有几种方法,可以从他们的选择中派生呈现模板所需的项目。最重要的是它为每个选择创建一个 URL。它通过一些字符串修饰等来做到这一点。如果我想测试 Upsell.get_urls() 方法,我希望它不依赖于固定装置中选择的值,并且我不希望它依赖于灯具中的产品。

现在我在 setUp 方法中为测试填充数据库,这与 Django 每次都退出事务的方式很好,但仅限于 setUp 和 tearDown 之外。这工作得很好,除了一些模型设置起来相当复杂,而我实际上只需要为它获取一个属性。

我不能给你一个例子,因为我无法完成它,但这是我现在正在做的事情的类型。基本上我输入了一个完整的订单,创建了它所附加的 A/B 实验,等等。这还不包括由夹具设置的产品、类别等。这不是我关心的额外工作,因为我什至不能一次测试一个基于数据库的对象。下面的测试很重要,但它们是集成测试。我想通过分别测试每个项目来建立这样的东西。正如您所指出的,也许我不应该选择与数据库紧密相关的框架。像这样的东西是否存在任何类型的依赖注入? (超出我的测试范围,但代码本身也是如此)

class TestMultiSinglePaySwap(TestCase):
    fixtures = ['/srv/asm/fixtures/alchemysites.json','/srv/asm/fixtures/catalog.json','/srv/asm/fixtures/checkout_smallset.json','/srv/asm/fixtures/order-test-fixture.json','/srv/asm/fixtures/offers.json']

def setUp(self):
    self.o = Order()
    self.sp = SiteProfile.objects.get(pk=1)
    self.c = Customer.objects.get(pk=1)
    signals.post_save.disconnect(order_email_first, sender=Order)
    self.o.customer = self.c
    p = Payment()
    p.cc_number = '4444000011110000'
    p.cc_exp_month = '12'
    p.cc_type = 'V'
    p.cc_exp_year = '2020'
    p.cvv2 = '123'
    p.save()
    self.o.payment = p
    self.o.site_profile = self.sp
    self.o.save()
    self.initial_items = []
    self.main_kit = Product.objects.get(pk='MOA1000D6')
    self.initial_items.append(self.main_kit)
    self.o.add_item('MOA1000D6', 1, False)
    self.item1 = Product.objects.get(pk='MOA1041A-6')
    self.initial_items.append(self.item1)
    self.o.add_item('MOA1041A-6', 1, False)
    self.item2 = Product.objects.get(pk='MOA1015-6B')
    self.initial_items.append(self.item2)
    self.o.add_item('MOA1015-6B', 1, False)
    self.item3 = Product.objects.get(pk='STP1001-6E')
    self.initial_items.append(self.item3)
    self.o.add_item('STP1001-6E', 1, False)
    self.swap_item1 = Product.objects.get(pk='MOA1041A-1')

def test_single_pay_swap_wholeorder(self):
    o = self.o
    swap_all_skus(o)
    post_swap_order = Order.objects.get(pk = o.id)
    swapped_skus = ['MOA1000D','MOA1041A-1','MOA1015-1B','STP1001-1E']
    order_items = post_swap_order.get_all_line_items()
    self.assertEqual(order_items.count(), 4)
    pr1 = Product()
    pr1.sku = 'MOA1000D'
    item = OrderItem.objects.get(order = o, sku = 'MOA1000D') 
    self.assertTrue(item.sku.sku == 'MOA1000D')
    pr2 = Product()
    pr2.sku = 'MOA1015-1B'
    item = OrderItem.objects.get(order = o, sku = 'MOA1015-1B') 
    self.assertTrue(item.sku.sku == 'MOA1015-1B')
    pr1 = Product()
    pr1.sku = 'MOA1041A-1'
    item = OrderItem.objects.get(order = o, sku = 'MOA1041A-1') 
    self.assertTrue(item.sku.sku == 'MOA1041A-1')
    pr1 = Product()
    pr1.sku = 'STP1001-1E'
    item = OrderItem.objects.get(order = o, sku = 'STP1001-1E') 
    self.assertTrue(item.sku.sku == 'STP1001-1E')

请注意,尽管我尝试过,但我从未真正使用过 Mock 框架。所以我也可能只是从根本上错过了一些东西。

【问题讨论】:

    标签: django unit-testing django-models mocking


    【解决方案1】:

    查看model mommy。它可以使用外键自动创建对象。

    【讨论】:

    • 这是我的下一步。在我现在工作的地方,我们做的数据库测试要少得多,但是我们处于绝对不能使用固定装置的情况。所以我正在寻找模特妈妈和工厂身材。
    • 你成就了我的一天。谢谢,MikeTwo!
    【解决方案2】:

    这可能无法回答您的问题,但可能会让您深思。

    在我看来,当您测试数据库支持的项目或应用程序时,您可以模拟的内容是有限的。当您使用框架和 ORM(例如 Django 提供的 ORM)时尤其如此。在 Django 中,业务模型类和持久性模型类之间没有区别。如果您想要这样的区别,那么您必须自己添加它。

    除非您愿意自己添加额外的复杂层,否则单独测试业务对象而不必添加固定装置等变得很棘手。如果必须这样做,您将不得不处理 Django 完成的一些自动魔术伏都教.

    如果您确实选择咬紧牙关深入研究,那么 Michael Foord 的 Python Mock library 会派上用场。

    我正在尽最大努力采用 TDD 作为我的主要工作方法,但使用 Django 的工作方式,您似乎可以运行非常琐碎的单元测试,或者这些大规模的集成测试。

    我使用 Django 单元测试机制来编写非平凡的单元测试。毫无疑问,我的要求和你的很不一样。如果您可以提供有关您要完成的工作的更具体的详细信息,那么此处的用户将能够建议其他替代方案。

    【讨论】:

    • 我认为当您说“业务模型类和持久性模型类之间没有区别”时,您一针见血。 Django 为您提供了测试数据库选项,这与预期的模拟一样接近(尽管可能有一个“模拟”数据库后端,它只会强制执行您想要的约束)。至少现在我知道我不是在寻找已经写好的东西。
    • 你可能还想看看 David Cramer 的 github.com/dcramer/mock-django BTW,github 上有一个 mock 库的分支:github.com/yujiabe/mock(2012 年 6 月更新)
    • FWIW,我采纳了加入 Mock 和 mock-django 的建议,并完成了很多我想做的事情。 Mock 库需要一些学习如何正确使用它,但它非常值得。所以我只是想更新一下,这两条建议在金钱上是正确的,2-3 年后,我每天都使用这些方法来编写非平凡的单元测试。
    猜你喜欢
    • 2017-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-22
    • 2015-03-26
    • 2021-08-06
    • 1970-01-01
    相关资源
    最近更新 更多