Factory-boy / Django - 工厂实例不反映对模型实例的更改
Factory-boy / Django - Factory instance not reflecting changes to model instance
我正在为我正在开发的网站编写测试,我用 factoryboy
工厂对象表示模型。
但是,我 运行 的一些行为让我感到有些困惑,我想知道这里是否有人愿意向我解释一下
我正在运行进行包含以下模型的测试:
STATUS = (
('CALCULATING'),
('PENDING'),
('BUSY'),
('SUCCESS'),
('FAILED')
)
class SchoolImport(models.Model):
date = models.DateTimeField(auto_now_add=True)
status = models.CharField(
verbose_name=_('status'), choices=STATUS,
max_length=50, default='CALCULATING'
)
为此我创建了以下工厂。如您所见,status
设置为其默认值,我发现这比随机选择值
更真实
class SchoolImportFactory(factory.DjangoModelFactory):
class Meta:
model = models.SchoolImport
status = 'CALCULATING'
school = factory.SubFactory(SchoolFactory)
@factory.lazy_attribute
def date(self):
return timezone.now() - datetime.timedelta(days=10)
下面您将看到正在测试的函数的(简化)版本以及测试本身。 (我目前已经注释掉了笔记本电脑上的所有其他代码,因此您在下面看到的函数是准确的表示)
它的要点是该函数接收一个 id
值,它将使用该值从数据库中获取一个 SchoolImport
对象并更改其状态。该函数在 celery 中将是 运行,因此 return 什么都没有。
当我 运行 通过调试器进行此测试时,我可以看到值已正确更改。但是,当测试 运行 的最终断言失败时,因为 self.school_import.status
仍然等于 CALCULATING
.
#app.utils.py
def process_template_objects(school_import_pk):
school_import = models.SchoolImport.objects.get(id=import_file_pk)
school_import.status = 'BUSY'
school_import.save()
#app.tests.test_utils.py
class Test_process_template_objects_function(TestCase):
def setUp(self):
self.school = SchoolFactory()
self.school_import = SchoolImportFactory(
school=self.school
)
def test_function_alters_school_import_status(self):
self.assertEqual(
self.school_import.status, 'CALCULATING'
)
utils.process_template_objects(self.school_import.id)
self.assertNotEqual(
self.school_import.status, 'CALCULATING'
)
当我 运行 通过调试器(在断言失败之前有一个断点)进行此测试并且 运行 SchoolImport.objects.get(id=self.school_import.id).status
它 return 正确 BUSY
值。
因此,尽管 FactoryInstance 所表示的对象正在正确更新,但更改并未反映在工厂实例本身中。
虽然我意识到我可能在这里做错了什么/遇到了预期的行为,但我想知道使用 factoryboy fget 编写测试的人如何绕过这种行为,或者是否有办法 'refresh' factoryboy 实例来反映对模型实例的更改。
问题来自于这样一个事实,即在 process_template_objects
中,您使用的 SchoolImport
对象实例与测试中的实例不同。
如果你运行:
a = models.SchoolImport.objects.get(pk=1)
b = models.SchoolImport.objects.get(pk=2)
assert a == b # True: both refer to the same object in the database
assert a is b # False: different Python objects, each with its own memory
a.status = 'SUCCESS'
a.save()
assert a.status == 'SUCCESS' # True: it was indeed changed in this Python object
assert b.status == 'SUCCESS' # False: the 'b' object hasn't seen the change
为了解决这个问题,您应该在调用 process_template_objects
:
后从数据库中重新获取实例
utils.process_template_objects(self.school_import.id)
self.school_import.refresh_from_db()
请参阅 https://docs.djangoproject.com/en/2.2/ref/models/instances/#refreshing-objects-from-database 以获得更详细的解释!
如果您从模型实例中删除一个字段,再次访问它会从数据库中重新加载该值。
obj = MyModel.objects.first()
del obj.field
obj.field # Loads the field from the database
见https://docs.djangoproject.com/en/2.2/ref/models/instances/#refreshing-objects-from-database
我正在为我正在开发的网站编写测试,我用 factoryboy
工厂对象表示模型。
但是,我 运行 的一些行为让我感到有些困惑,我想知道这里是否有人愿意向我解释一下
我正在运行进行包含以下模型的测试:
STATUS = (
('CALCULATING'),
('PENDING'),
('BUSY'),
('SUCCESS'),
('FAILED')
)
class SchoolImport(models.Model):
date = models.DateTimeField(auto_now_add=True)
status = models.CharField(
verbose_name=_('status'), choices=STATUS,
max_length=50, default='CALCULATING'
)
为此我创建了以下工厂。如您所见,status
设置为其默认值,我发现这比随机选择值
class SchoolImportFactory(factory.DjangoModelFactory):
class Meta:
model = models.SchoolImport
status = 'CALCULATING'
school = factory.SubFactory(SchoolFactory)
@factory.lazy_attribute
def date(self):
return timezone.now() - datetime.timedelta(days=10)
下面您将看到正在测试的函数的(简化)版本以及测试本身。 (我目前已经注释掉了笔记本电脑上的所有其他代码,因此您在下面看到的函数是准确的表示)
它的要点是该函数接收一个 id
值,它将使用该值从数据库中获取一个 SchoolImport
对象并更改其状态。该函数在 celery 中将是 运行,因此 return 什么都没有。
当我 运行 通过调试器进行此测试时,我可以看到值已正确更改。但是,当测试 运行 的最终断言失败时,因为 self.school_import.status
仍然等于 CALCULATING
.
#app.utils.py
def process_template_objects(school_import_pk):
school_import = models.SchoolImport.objects.get(id=import_file_pk)
school_import.status = 'BUSY'
school_import.save()
#app.tests.test_utils.py
class Test_process_template_objects_function(TestCase):
def setUp(self):
self.school = SchoolFactory()
self.school_import = SchoolImportFactory(
school=self.school
)
def test_function_alters_school_import_status(self):
self.assertEqual(
self.school_import.status, 'CALCULATING'
)
utils.process_template_objects(self.school_import.id)
self.assertNotEqual(
self.school_import.status, 'CALCULATING'
)
当我 运行 通过调试器(在断言失败之前有一个断点)进行此测试并且 运行 SchoolImport.objects.get(id=self.school_import.id).status
它 return 正确 BUSY
值。
因此,尽管 FactoryInstance 所表示的对象正在正确更新,但更改并未反映在工厂实例本身中。
虽然我意识到我可能在这里做错了什么/遇到了预期的行为,但我想知道使用 factoryboy fget 编写测试的人如何绕过这种行为,或者是否有办法 'refresh' factoryboy 实例来反映对模型实例的更改。
问题来自于这样一个事实,即在 process_template_objects
中,您使用的 SchoolImport
对象实例与测试中的实例不同。
如果你运行:
a = models.SchoolImport.objects.get(pk=1)
b = models.SchoolImport.objects.get(pk=2)
assert a == b # True: both refer to the same object in the database
assert a is b # False: different Python objects, each with its own memory
a.status = 'SUCCESS'
a.save()
assert a.status == 'SUCCESS' # True: it was indeed changed in this Python object
assert b.status == 'SUCCESS' # False: the 'b' object hasn't seen the change
为了解决这个问题,您应该在调用 process_template_objects
:
utils.process_template_objects(self.school_import.id)
self.school_import.refresh_from_db()
请参阅 https://docs.djangoproject.com/en/2.2/ref/models/instances/#refreshing-objects-from-database 以获得更详细的解释!
如果您从模型实例中删除一个字段,再次访问它会从数据库中重新加载该值。
obj = MyModel.objects.first()
del obj.field
obj.field # Loads the field from the database
见https://docs.djangoproject.com/en/2.2/ref/models/instances/#refreshing-objects-from-database