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

如何Mock pyodbc cursor.messages以覆盖单元测试分支逻辑

如何Mock pyodbc cursor.messages实现execute_read方法分支全覆盖

我需要测试execute_read方法的if/else分支逻辑,但始终无法正确Mockcursor.messages的条件分支,现有单元测试只覆盖了部分场景,希望实现全分支覆盖。相关代码如下:

def execute_read(self, query):
    dbconn = pyodbc.connect(self.connection_string, convert_unicode=True)
    with dbconn.cursor() as cur:
        cursor = cur.execute(query)
        if not cursor.messages:
            res = cursor.fetchall()
        else:
            raise Exception(cursor.messages[0][1])
        return res

# 现有单元测试
@patch.object(pyodbc, 'connect')
def test_execute_read(self, pyodbc_mock):
    pyodbc_mock.return_value = MagicMock()
    self.assertIsNotNone(execute_read('query'))

解决方案

要覆盖两个分支,需要精准Mock pyodbc的调用链,包括连接对象、游标对象,以及execute返回实例的messages属性和fetchall方法。

测试if分支(无消息,正常返回结果)

让cursor.messages为空列表,触发正常返回逻辑:

from unittest.mock import patch, MagicMock
import unittest
import pyodbc
# 替换成实际包含execute_read的类所在模块
from your_module import YourDBClass

class TestExecuteRead(unittest.TestCase):
    @patch.object(pyodbc, 'connect')
    def test_execute_read_normal_case(self, mock_connect):
        # Mock数据库连接
        mock_dbconn = MagicMock()
        mock_connect.return_value = mock_dbconn
        
        # Mock游标(处理with语句的__enter__)
        mock_cur = MagicMock()
        mock_dbconn.cursor.return_value.__enter__.return_value = mock_cur
        
        # Mock execute返回的游标实例,设置messages为空
        mock_execute_cursor = MagicMock()
        mock_execute_cursor.messages = []
        mock_execute_cursor.fetchall.return_value = [('id1', 'name1'), ('id2', 'name2')]
        mock_cur.execute.return_value = mock_execute_cursor
        
        # 执行测试
        db_instance = YourDBClass()
        result = db_instance.execute_read('SELECT * FROM users')
        
        # 断言
        self.assertEqual(result, [('id1', 'name1'), ('id2', 'name2')])
        mock_cur.execute.assert_called_once_with('SELECT * FROM users')
        mock_execute_cursor.fetchall.assert_called_once()

测试else分支(有消息,抛出异常)

让cursor.messages包含错误信息,触发异常抛出逻辑:

@patch.object(pyodbc, 'connect')
    def test_execute_read_error_case(self, mock_connect):
        # Mock数据库连接
        mock_dbconn = MagicMock()
        mock_connect.return_value = mock_dbconn
        
        # Mock游标
        mock_cur = MagicMock()
        mock_dbconn.cursor.return_value.__enter__.return_value = mock_cur
        
        # Mock execute返回的游标实例,设置错误消息
        mock_execute_cursor = MagicMock()
        # 模拟pyodbc的messages格式:(状态码, 错误信息)
        mock_execute_cursor.messages = [(100, 'Invalid SQL syntax')]
        mock_cur.execute.return_value = mock_execute_cursor
        
        # 执行测试并断言异常
        db_instance = YourDBClass()
        with self.assertRaises(Exception) as exc_context:
            db_instance.execute_read('INVALID SQL')
        
        self.assertEqual(str(exc_context.exception), 'Invalid SQL syntax')
        mock_cur.execute.assert_called_once_with('INVALID SQL')

关键注意点

  • with dbconn.cursor() as cur会调用游标对象的__enter__方法,所以必须Mock这个方法的返回值为游标实例。
  • cur.execute(query)返回的是pyodbc游标对象本身,需要单独Mock这个返回实例的messages属性,而不是外层的mock_cur的messages。
  • 严格按照调用层级Mock:pyodbc.connect → 连接对象 → 游标对象 → execute返回的游标实例。

内容的提问来源于stack exchange,提问作者vermachint

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 18:03:28