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

zen*_*sel 9 django unit-testing mocking django-models

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

如果我有一个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')
Run Code Online (Sandbox Code Playgroud)

请注意,虽然我已经尝试过,但我从未真正使用过Mock框架.所以我也可能在这里缺少一些东西.

Mik*_*Two 6

看看模特妈妈。它可以自动创建带有外键的对象。


Man*_*dan 4

这可能不会回答您的问题,但可能会给您带来思考。

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

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

如果您确实选择咬紧牙关并深入研究,那么 Michael Foord 的 Python Mock 库将会非常方便。

我正在尽最大努力采用 TDD 作为我的主要工作方法,但是从 Django 的工作方式来看,您似乎可以运行非常简单的单元测试,也可以运行这些大规模的集成测试。

我使用 Django 单元测试机制来编写重要的单元测试。我的要求无疑与你的非常不同。如果您可以提供有关您要实现的目标的更具体的详细信息,那么此处的用户将能够建议其他替代方案。