Lon*_*Dev 5 python django unit-testing
我有一个 Django 命令,它运行一个循环,直到数据库可用:
import time
from django.db import connections
from django.db.utils import OperationalError
from django.core.management.base import BaseCommand
class Command(BaseCommand):
"""Django command to pause execution until database is available"""
def handle(self, *args, **options):
"""Handle the command"""
self.stdout.write('Waiting for database...')
db_conn = None
while not db_conn:
try:
db_conn = connections['default']
except OperationalError:
self.stdout.write('Database unavailable, waiting 1 second...')
time.sleep(0.1)
self.stdout.write(self.style.SUCCESS('Database available!'))
Run Code Online (Sandbox Code Playgroud)
我想为此代码创建单元测试。
我已经设法从一开始就测试可用的数据库,如下所示:
def test_wait_for_db_ready(self):
"""Test waiting for db when db is available"""
with patch('django.db.utils.ConnectionHandler.__getitem__') as gi:
gi.return_value = True
call_command('wait_for_db')
self.assertTrue(True)
Run Code Online (Sandbox Code Playgroud)
有没有办法测试命令在返回之前等待数据库可用?
到目前为止,我已经尝试了以下操作,但是它不起作用,因为attempt在getitem.
def test_wait_for_db(self):
"""Test waiting for db"""
attempt = 0
def getitem(alias):
if attempt < 5:
attempt += 1
raise OperationalError()
else:
return True
with patch('django.db.utils.ConnectionHandler.__getitem__') as gi:
gi.side_effect = getitem
call_command('wait_for_db')
self.assertGreaterEqual(attempt, 5)
Run Code Online (Sandbox Code Playgroud)
有几种方法可以实现这一点。最简单的方法可能只是放弃getitem()嵌套函数并使用OperationalErrors序列设置副作用。然后,您可以使用修补gi对象的call_count. 例如:
def test_wait_for_db(self):
"""Test waiting for db"""
with patch('django.db.utils.ConnectionHandler.__getitem__') as gi:
gi.side_effect = [OperationalError] * 5 + [True]
call_command('wait_for_db')
self.assertGreaterEqual(gi.call_count, 5) # Verify using the call_count
Run Code Online (Sandbox Code Playgroud)
如果您希望保留该getitem()函数,那么我认为您只需要创建attempt变量nonlocal即可在嵌套函数中看到它:
def test_wait_for_db(self):
"""Test waiting for db"""
attempt = 0
def getitem(alias):
nonlocal attempt # Make the outer attempt variable visible
if attempt < 5:
attempt += 1
raise OperationalError()
else:
return True
with patch('django.db.utils.ConnectionHandler.__getitem__') as gi:
gi.side_effect = getitem
call_command('wait_for_db')
self.assertGreaterEqual(attempt, 5)
Run Code Online (Sandbox Code Playgroud)
第三,正如评论中所建议的,您可以创建一个具有attempt属性的类,并使用该类的实例作为副作用:
def test_wait_for_db(self):
"""Test waiting for db"""
class Getitem:
def __init__(self):
self.attempt = 0
def __call__(self, item):
if self.attempt < 5:
self.attempt += 1
raise OperationalError()
else:
return True
with patch('django.db.utils.ConnectionHandler.__getitem__') as gi:
getitem = Getitem()
gi.side_effect = getitem
call_command('wait_for_db')
self.assertGreaterEqual(getitem.attempt, 5) # Access the attempts from the instance
Run Code Online (Sandbox Code Playgroud)