在Django测试中,我应该如何保存数据库对象然后从数据库中检索它?

时间:2015-08-08 21:03:30

标签: python django

我正在使用Django 1.8。我编写了以下代码来测试pre_save挂钩是否正常工作,但这段代码似乎非常不优雅。这是"正确的方式"写这种单元测试?

class PreSaveTests(TestCase):
    def test_pre_save_hook(self):
        person = Person(name="Joe")
        person.save()
        person2 = Person.objects.get(pk = person.pk)
        # Confirm that the pre_save hook ran.
        # The hook sets person.is_cool to True.
        self.assertEqual(person2.is_cool, True)

这很好用,但看起来很难看。

真正丑陋的部分是personperson2是同一个数据库对象。唯一的区别是从数据库中检索到person2

5 个答案:

答案 0 :(得分:5)

你在考试中所做的一切都很好。不过,我认为你可以简化/改进它。

我认为你应该使用工厂(你可以使用FactoryBoy)。这样,在模型上添加/删除必填字段时,您无需更新测试。此外,您可以从测试中删除不相关的信息。在这种情况下,人名为Joe的事实完全无关紧要。

您可以替换:

person = Person(name="Joe")
person.save()

使用:

person = PersonFactory.create()

正如Daniel所说,你不需要重新加载Person实例。所以你不必这样做:

person2 = Person.objects.get(pk = person.pk)

最后,小提示,您可以使用assertTrue代替assertEquals(something, True)

class PreSaveTests(TestCase):

    def test_pre_save_hook(self):
        person = PersonFactory.create()
        self.assertTrue(person.is_cool)

答案 1 :(得分:4)

首先,我不确定为什么你认为丑陋:这似乎是测试这种功能的完美合理方式。

然而,你绝对可以让它变得更简单。虽然Django实例没有身份 - 也就是说,从数据库中检索的两个实例分别不会共享修改,直到它们被保存和检索 - 当预保存挂钩运行时,它会修改现有实例。实际上is_cool 进行修改以设置person2,因此无需检索并检查Intent intent = new Intent(); ComponentName cn = new ComponentName("com.whatsapp", "com.whatsapp.Main"); intent.setComponent(cn); PendingIntent pending = PendingIntent.getActivity(context, 0, intent, 0); RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.activity_main); views.setOnClickPendingIntent(R.id.imageView, pending); appWidgetManager.updateAppWidget(currentWidgetId, views);

答案 2 :(得分:1)

我认为这是测试简单功能的好方法。然而,这本书的编号是#34;通过模拟数据库功能可以更好地定义单元测试。 这样,您可以对方法进行单元测试,而无需关心数据库正在做什么。

我通常使用模拟库(包含在3.x中)执行此操作。没有详细介绍其他答案中的详细信息,您可以使用补丁来模拟您正在测试的模型(Person),然后让它返回一些东西。

看看mock-django,它提供了许多与此相关的功能,https://pypi.python.org/pypi/mock-djangohttps://docs.python.org/3/library/unittest.mock.html

对于Python 3,我无法对此进行测试(并且我会更加明确地说明这一点)。在单元测试类中,您可以创建这样的测试。

# first patch your class
@patch('my_app_name.models.Person')
def test_my_person(self, person_mock)
    person_mock.objects = MagicMock()
    person_mock.objects.configure_mock(get.return_value='guy_number_1')
    # then you can test your method. For example if your method change the guy name.
    self.assertEquals(my_method('guy_number_1'), 'GUY_NUMBER_1')

代码不是最好的,但你的想法是你在嘲笑数据库,所以如果你的数据库连接刹车,你的单元测试不应该(因为你应该没有测试) Django功能也不是你的数据库连接。)

这在我进行自动构建和测试时非常有用,无需部署测试数据库。然后,您可以添加集成测试以涵盖数据库功能。

如果不够清楚,我会扩展解释。

有时在mock中忽略的有用的东西是configure方法,side_effect用于模拟异常,有时你需要重新加载模块来应用补丁。

答案 3 :(得分:1)

您可以直接检查查询中的属性,而无需实际获取对象:

class PreSaveTests(TestCase):
    def test_pre_save_hook(self):
        person = Person(name="Joe")
        person.save()
        # Confirm that the pre_save hook ran.
        # The hook sets person.is_cool to True.
        self.assertTrue(
            Person.objects.filter(pk = person.pk, is_cool=True).exists()
        )

答案 4 :(得分:0)

这个问题有点晚了,但 Django 3.2 中有一个 refresh_from_db() 函数,

所以你可以运行:

person = Person(name="Geof")
person.save()
person.refresh_from_db()

https://docs.djangoproject.com/en/3.2/ref/models/instances/#refreshing-objects-from-database