Python 在Django测试中,我应该如何保存数据库对象,然后从数据库中检索它?
我正在使用Django 1.8。我编写了下面的代码来测试Python 在Django测试中,我应该如何保存数据库对象,然后从数据库中检索它?,python,django,Python,Django,我正在使用Django 1.8。我编写了下面的代码来测试pre_savehook是否正常工作,但这段代码似乎非常不雅观。这是编写这种单元测试的“正确方法”吗 class PreSaveTests(TestCase): def test_pre_save_hook(self): person = Person(name="Joe") person.save() person2 = Person.objects.get(pk = person.
pre_save
hook是否正常工作,但这段代码似乎非常不雅观。这是编写这种单元测试的“正确方法”吗
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)
这很好,但看起来很难看
真正难看的是
person
和person2
是同一个数据库对象。唯一的区别是person2
是从数据库中检索的。首先,我不知道你为什么认为这很难看:这似乎是测试此功能的一种完全合理的方法
然而,你肯定可以让它更简单。虽然Django实例没有标识(即,分别从数据库检索的两个实例在保存和检索它们之前不会共享修改),但当预保存挂钩运行时,它会修改现有实例。因此,事实上,
person
将得到修改,以设置很酷
,因此无需检索和检查person2
,我认为这是测试简单功能的好方法。但是,通过模拟数据库功能更好地定义“按书”单元测试。
通过这种方式,您可以对方法进行单元测试,而不必关心数据库在做什么
我通常使用模拟库(包含在3.x中)来实现这一点。不必像其他答案中描述的那样详细说明,您可以使用补丁来模拟您正在测试的模型(Person),然后让它返回一些东西
看看mock django,它提供了许多与此相关的功能,这里
对于Python3,我无法对此进行测试(并且我将使其更加明确,我通常会这样做)。在unittest类中,您可以创建这样的测试
# 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')
这段代码不是最好的,但其思想是您在模拟数据库,因此如果您的数据库连接停止,那么您的unittest不会停止(这应该是因为您没有测试Django功能或数据库连接) 这对于我在不部署测试数据库的情况下进行自动构建和测试非常有用。然后,您可以添加集成测试来覆盖数据库功能 如果解释不够清楚,我就扩大解释范围
mock中有时忽略的有用的东西是configure方法、mock异常的副作用,有时您需要重新加载模块以应用修补程序。您可以直接检查查询中的属性,而无需实际获取对象:
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()
)
你在测试中所做的一切都很好。不过,我认为你可以简化/改进一下 我认为你应该使用工厂(你可以使用
FactoryBoy
)。这样,当您在模型上添加/删除必填字段时,就不必更新测试。此外,您还可以从测试中删除不相关的信息。在本例中,人名是Joe
这一事实完全无关
您可以替换:
person = Person(name="Joe")
person.save()
与:
正如Daniel提到的,您不需要重新加载Person实例。所以你不必这么做:
person2 = Person.objects.get(pk = person.pk)
最后,一个小提示,您可以使用assertTrue
而不是assertEquals(something,True)
:
关于这个问题有点晚了,但Django 3.2中有一个
refresh\u from_db()
函数
因此,您可以运行:
person=person(name=“Geof”)
person.save()
person.refresh_from_db()
@zoom pro能否为该特定示例显示一些代码?你会模拟
save()
方法,让它调用pre\u save
钩子吗?在那种情况下,测试不会测试任何东西吗?示例:钩子可以被移除,测试仍然会通过…@zoom pro谢谢。因此,您可以在保存之前直接测试调用的方法,而不影响数据库——这很好——但问题仍然存在:如果删除了钩子,测试仍然会通过。此外,如果该值不符合is_cool约束,测试也将通过。我不认为单元测试+集成测试有什么意义。“如果您的数据库连接中断,那么您的单元测试就不会”=>您有过这样的问题吗?大多数情况下发生在进行自动构建、测试和部署时。最典型的是psycopg2无法安装(它非常脆弱)。然后,无论数据库在做什么,都可以运行单元测试,这样就可以很方便地快速调试问题。哦,我明白了-在大多数情况下,您可能想使用SQLite,不是吗?在某些情况下,这可能是一个有效的观点。为什么它会被否决?除非你想验证你没有意外地实现一个钩子(我想这只会影响实例),而不是一个预保存钩子。das-g已经正确地找到了我从数据库加载person2的原因。我希望确保更改在数据库中,而不仅仅是在Python对象上。意外地使用post_save钩子是不太可能的,但是可能还有其他一些奇怪的错误会导致这个单元测试失败。我希望单元测试是彻底的,我希望它捕获我几乎无法想象的错误。
person2 = Person.objects.get(pk = person.pk)
class PreSaveTests(TestCase):
def test_pre_save_hook(self):
person = PersonFactory.create()
self.assertTrue(person.is_cool)