使用SQLite3的Django LiveServerTestCase测试重复执行异常
这个问题我之前也碰到过,核心原因是LiveServerTestCase启动的独立HTTP服务器和你的测试代码不在同一个数据库事务上下文里。Django普通的TestCase会用数据库事务来隔离测试,测试结束后自动回滚数据,但LiveServerTestCase因为启动了真实的Web服务器进程,测试进程里的事务操作对服务器进程是可见的,而且默认不会自动清理测试数据——所以第一次测试创建的用户会留在数据库里,第二次运行测试时就会触发“用户已存在”的校验失败。
下面是几个实用的解决办法:
1. 每次测试前后手动清理数据库
你可以在测试类的setUp或tearDown方法里,主动清空数据库或者删除测试生成的用户数据。比如用Django的flush命令来重置数据库:
from django.test import LiveServerTestCase from selenium.webdriver.common.by import By from django.contrib.auth.models import User from django.core.management import call_command class UserRegistrationTest(LiveServerTestCase): def setUp(self): super().setUp() self.driver = webdriver.Chrome() # 初始化你的Selenium驱动 # 测试开始前清空数据库,避免残留数据 call_command('flush', verbosity=0, interactive=False) def tearDown(self): # 也可以针对性删除测试创建的用户,比flush更高效 User.objects.filter(username='test_reg_user').delete() self.driver.quit() super().tearDown() def test_successful_registration(self): # 访问注册页面 self.driver.get(f"{self.live_server_url}/accounts/register/") # 填写注册表单 self.driver.find_element(By.NAME, 'username').send_keys('test_reg_user') self.driver.find_element(By.NAME, 'email').send_keys('test@example.com') self.driver.find_element(By.NAME, 'password1').send_keys('SecurePass123!') self.driver.find_element(By.NAME, 'password2').send_keys('SecurePass123!') # 提交表单 self.driver.find_element(By.CSS_SELECTOR, 'button[type="submit"]').click() # 断言用户已创建 self.assertTrue(User.objects.filter(username='test_reg_user').exists())
2. 使用内存SQLite数据库
把测试用的数据库改成内存模式,这样每次测试运行时都会创建一个全新的数据库,测试结束后自动销毁,完全不会有数据残留问题。在你的settings.py里添加这样的配置:
import sys if 'test' in sys.argv: DATABASES['default'] = { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:', }
这个方案的好处是不用手动处理数据清理,而且内存数据库的读写速度更快,测试运行效率更高。
3. 改用pytest-django的live_server fixture(如果你用pytest)
如果你的项目用pytest做测试框架,pytest-django插件的live_server fixture会自动帮你处理数据库隔离,每次测试后都会清理数据,不用自己写额外的代码。你只需要安装插件,然后在测试函数里注入live_server fixture:
from selenium.webdriver.common.by import By from django.contrib.auth.models import User def test_user_registration(live_server, selenium): selenium.get(f"{live_server.url}/accounts/register/") # 填写表单逻辑和之前一样 selenium.find_element(By.NAME, 'username').send_keys('test_user') # ... 其他表单填写步骤 selenium.find_element(By.CSS_SELECTOR, 'button[type="submit"]').click() assert User.objects.filter(username='test_user').exists()
为什么删除数据库文件能临时解决问题?
因为删除数据库文件后,你重新执行迁移会创建一个全新的空数据库,第一次测试时没有残留用户数据,所以能成功。但第二次测试时,第一次创建的用户已经被写入数据库了,所以就会触发重复用户的错误。
内容的提问来源于stack exchange,提问作者briancaffey

