模拟 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
我提出了以下解决方案:
- 将 lib1.selectSQL(cxnx, my_query) 打印到文件
- 将lib1.selectSQL中的数据插入namedtuple
,
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:
的信息
- https://github.com/mkleehammer/pyodbc/blob/master/tests2/informixtests.py
- https://github.com/mkleehammer/pyodbc/wiki/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 对我有效。
对依赖于 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
我提出了以下解决方案:
- 将 lib1.selectSQL(cxnx, my_query) 打印到文件
- 将lib1.selectSQL中的数据插入namedtuple
,
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:
的信息- https://github.com/mkleehammer/pyodbc/blob/master/tests2/informixtests.py
- https://github.com/mkleehammer/pyodbc/wiki/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 对我有效。