tia*_*rtr 5 python django unit-testing mocking pyodbc
我想对一些使用自定义 pyodbc 数据库连接的 django 视图进行单元测试
from django.http import JsonResponse, HttpResponseNotFound, HttpResponseBadRequest, HttpResponseServerError, HttpResponseForbidden
from django.core.exceptions import SuspiciousOperation
from django.utils.datastructures import MultiValueDictKeyError
import os
import pyodbc
# Create your views here.
db_credentials = os.environ.get('DATABASE_CREDENTIALS')
dbh = pyodbc.connect(db_credentials)
def get_domains(request):
if request.method == 'GET':
args = request.GET
elif request.method == 'POST':
args = request.POST
try:
cursor = dbh.cursor()
if 'owner' in args:
owner = args['owner']
cursor.execute('{call GET_DOMAINS_FOR_OWNER(?)}', owner)
else:
cursor.execute('{call GET_DOMAINS()}')
result = cursor.fetchall()
if(result):
return JsonResponse([row[0] for row in result], safe=False)
else:
return JsonResponse([], safe=False)
except pyodbc.Error as e:
return HttpResponseServerError(e)
except SuspiciousOperation as e:
return HttpResponseForbidden(e)
Run Code Online (Sandbox Code Playgroud)
由于我不希望单元测试访问数据库,因此我如何模拟这种行为:
这是我的测试驱动程序
from django.test import SimpleTestCase
from sms_admin import *
# Create your tests here.
HTTP_OK = 200
HTTP_NOTFOUND = 404
class AdminTestCase(SimpleTestCase):
"""docstring for AdminTestCase"""
def test_get_pool_for_lds(self):
response = self.client.get('/sms_admin/get_pool_for_lds', {'domain': 'sqlconnect', 'stage': 'dev', 'lds': 'reader'})
self.assertEqual(response.content, b'pdss_reader')
self.assertEqual(response.status_code, HTTP_OK)
Run Code Online (Sandbox Code Playgroud)
您可以pyodbc.connect不受任何限制地进行修补,如以下示例所示:
import pyodbc
from unittest.mock import patch
with patch("pyodbc.connect") as mock_connect:
pyodbc.connect("Credentials")
mock_connect.assert_called_with("Credentials")
Run Code Online (Sandbox Code Playgroud)
现在真正的问题view.py是这条线
dbh = pyodbc.connect(db_credentials)
Run Code Online (Sandbox Code Playgroud)
该行在您导入时 执行,view.py并且您无法控制它,除非在您的测试代码中实施某种黑客攻击,例如在导入之前修补连接view.py或其他任何导入它。
我强烈建议您不要编写这种肮脏的技巧,并只更改一点代码来实现惰性dbh属性。另一种方法可以编写您自己的 db 类包装器(更好)并在您的测试中修补它,但这是一个强大的设计更改,您可以稍后通过实现测试的功能引入它。
在view.py使用:
_dbh = None
def get_db():
global _dbh
if _dbh is None:
_dbh = pyodbc.connect(db_credentials)
return _dbh
Run Code Online (Sandbox Code Playgroud)
哪里cusror变成
cursor = get_db().cursor()
Run Code Online (Sandbox Code Playgroud)
现在您可以在测试中修补get_db()和使用return_value模拟
class AdminTestCase(SimpleTestCase):
"""docstring for AdminTestCase"""
def setUp(self):
super().setUp()
p = patch("yourpackage.view.get_db")
self.addCleanup(p.stop)
self.get_db_mock = p.start()
self.db_mock = self.get_db_mock.return_value
self.cursor_mock = self.db_mock.cursor.return_value
def test_get_pool_for_lds(self, get_db_mock):
.... configure self.cursor_mock to behave as you need
response = self.client.get('/sms_admin/get_pool_for_lds', {'domain': 'sqlconnect', 'stage': 'dev', 'lds': 'reader'})
self.assertEqual(response.content, b'pdss_reader')
self.assertEqual(response.status_code, HTTP_OK)
Run Code Online (Sandbox Code Playgroud)
我遗漏了mock_cursor应该如何表现和游标调用断言的细节。您可以通过阅读mock框架文档来编写它。我曾经在setUp()方法中修补连接,因为我可以猜到你在这个类中几乎所有的测试中都需要它cursor_mock,db_mock并且get_db_mock可以用于不同的行为:我的经验是这种方法会在以后付出很多,而你会添加更多测试。
| 归档时间: |
|
| 查看次数: |
1670 次 |
| 最近记录: |