You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 07:20:19