Django原子事务单元测试失败:提交未回滚问题排查
问题描述
我有一个Django项目,包含如下两个模型:
# models.py from django.db import models, transaction class Person(models.Model): name = models.TextField() surname = models.TextField() class EmployeeInfo(models.Model): person = models.OneToOneField(Person, on_delete=models.CASCADE) employee_id = models.TextField(null=True, blank=True) @transaction.atomic def provision_employee_id(self): """Make atomic to ensure that two employees being provisioned at the same time do not get the same employee number""" self.employee_id = new_employee_number() self.save() raise Exception("forcing an exception") # Always raise an exception (for testing only)
对应的单元测试如下:
# tests.py class PersonTestCase(TestCase): def test_employee_id_atomic(self): person1 = Person(name="John", surname="Doe") employee_info = EmployeeInfo(person=person1) self.assertIsNone(employee_info.employee_id) # No employee_id yet. with self.assertRaises(Exception) as context: cluster_updated = employee_info.provision_employee_id() self.assertIsNone(employee_info.employee_id) # This FAILS
尽管用@transaction.atomic装饰了provision_employee_id()方法,但抛出异常后save()操作并未回滚,请问原因是什么?
原因分析
- 事务回滚的是数据库数据,不是内存对象:
@transaction.atomic确实会回滚数据库里的EmployeeInfo记录,但你测试中检查的是内存里employee_info对象的属性值。调用save()后,内存对象的employee_id已经被修改,哪怕数据库回滚,内存对象的属性不会自动还原。 - 对象未提前持久化到数据库:测试里的
person1和employee_info都只创建了内存对象,没调用save()存入数据库。provision_employee_id里的self.save()是第一次把对象写入数据库,事务回滚后数据库里不存在这条记录,但内存里的原对象已经被修改了employee_id,所以断言失败。 - 正确测试方式是重新查询数据库:要验证事务回滚效果,应该在异常抛出后,重新从数据库查询对应的
EmployeeInfo(或确认数据库中无此记录),而不是直接检查内存中的原对象。
内容的提问来源于stack exchange,提问作者rlandster
相关产品推荐
相关产品推荐

