模拟 pyodbc.Row 的 Pythonic 方式

Pythonic way to mock the pyodbc.Row

对依赖于 pyodbc 的 SQL 查询的函数进行正确单元测试的 pythonic 方法是什么?据我了解,最好的方法是模拟 returns 从 SQL 服务器输出的函数。问题是模拟应该 return?

我的设置: 在 lib1:

def selectSQL(connection, query):
    cursor = connection.cursor()
    cursor.execute(query)
    return cursor.fetchall()

在 lib2 中:

def function_to_be_tested(cxnx):
    my_query = "SELECT r1, r2 FROM t1"
    rows = lib1.selectSQL(cxnx, my_query)
    # do someting with the rows like:
    a = 0
    for row in rows
        a += row.r1 * row.r2
    return a

我提出了以下解决方案:

,

out_tuple   = namedtuple('out1', ["r1", "r2"])
printed_data = [(1,2),(2,3)]
out = [out_tuple(*row) for row in printed_data]

def test_mockSelectSQL(self):
    piotrSQL.selectSQL = MagicMock()
    piotrSQL.selectSQL.side_effect = [out]
    self.assertEqual(lib2.function_to_be_tested(True), 7)

我唯一担心的是模拟 returns namedtuple 而不是像原始函数那样的 pyodbc.Row。我检查了以下站点以搜索有关如何正确创建 pyodbc.Row:

的信息

在pyodbc的unittest中没有if的构造函数-我在源代码中也没有找到它(但我是新手所以我可能省略了它)......但是我在行上找到了以下信息文档:

However, there are some pyodbc additions that make them very convenient:

  • Values can be accessed by column name.

  • The Cursor.description values can be accessed even after the cursor is closed.

  • Values can be replaced.

  • Rows from the same select statement share memory.

因此,命名元组实际上与 pyodbc.Row 的行为方式相同(在访问值时)。在 pyodbc.Row 上进行单元测试是否有更 pythonic 的方法?可以假设这是一个很好的 Mock 吗?

根据@Nullman 在对该问题的评论中提出的建议,如果您想使用内存数据库,您可以尝试使用 SQLite ODBC 驱动程序,这样您就可以 return 实际 pyodbc.Row 像这样的对象:

import pyodbc
conn_str = 'Driver=SQLite3 ODBC Driver;Database=:memory:'
cnxn = pyodbc.connect(conn_str, autocommit=True)
crsr = cnxn.cursor()

# create test data
crsr.execute("CREATE TABLE table1 (id INTEGER PRIMARY KEY, dtm DATETIME)")
crsr.execute("INSERT INTO table1 (dtm) VALUES ('2017-07-26 08:08:08')")

# test retrieval
crsr.execute("SELECT * FROM table1")
print(crsr.fetchall())
# prints:
# [(1, datetime.datetime(2017, 7, 26, 8, 8, 8))]

crsr.close()
cnxn.close()

我刚刚测试了它,它在 Windows 的 PyCharm 对我有效。