如何使用外键测试Django中的模型

时间:2010-09-11 06:09:04

标签: django unit-testing django-models mocking

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

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

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

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

[编辑] 更明确的例子和更谦逊的

我的意思是我似乎只能运行琐碎的单元测试。我见过人们拥有非常好的测试和粒度模块。我确信其中一些可以追溯到糟糕的设计。

示例:

我有一个模型调用Upsell,它链接到Product模型。然后我有一个选择模型,它是Upsell的孩子(你想要#1,#2,#3门后面是什么)。

Upsell模型有几种方法可以派生从他们的选择中呈现模板所需的项目。最重要的一点是它为每个选择创建一个URL。它是通过一些字符串修改等来完成的。如果我想测试Upsell.get_urls()方法,我想让它不依赖于夹具中的选择值,我想不要依赖于它的值产品在灯具中。

现在我在setUp方法中填充db以进行测试,这与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框架。所以我也可能在这里缺少一些东西。

2 个答案:

答案 0 :(得分:4)

这可能不会回答你的问题,但它可能会让你深思熟虑。

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

除非你愿意自己添加额外的复杂层,否则单独测试业务对象变得棘手,而不必添加灯具等。如果你必须这样做,你将不得不解决Django所做的一些自动魔术vodoo

如果你确实选择咬牙切齿,那么Michael Foord的Python Mock library会非常方便。

  

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

我使用Django单元测试机制来编写非平凡的单元测试。我的要求无疑与你的要求截然不同。如果您可以提供有关您要完成的更具体的详细信息,那么这里的用户将能够建议其他替代方案。

答案 1 :(得分:4)

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