用Pythonic的方式模拟pyodbc.Row

4
“Pythonic”方式进行适当的单元测试取决于pyodbc所做SQL查询的功能。最佳方法是模拟返回来自SQL服务器的输出的函数。问题在于,模拟应该返回什么? 我的设置: 在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)

我唯一的担忧是模拟返回的是namedtuple而不是原始函数中的pyodbc.Row。我查看了以下网站,以寻找有关如何正确创建pyodbc.Row的信息:

在pyodbc的单元测试中,没有if的构造函数 - 我在源代码中也没有找到它(但我是新手,可能已经忽略了它)... 然而,我在Row文档中找到了以下信息:
“然而,有一些pyodbc的补充使它们非常方便:”
- 值可以通过列名访问。 - 即使在关闭游标后,也可以访问Cursor.description值。 - 值可以被替换。 - 来自同一select语句的行共享内存。
因此,似乎namedtuple实际上与pyodbc.Row(在访问值方面)表现相同。是否有更pythonic的方法来对pyodbc.Row进行单元测试?能否假设这是一个好的Mock?

1
我会创建一个内存 sqlite 表格,并填充模拟数据并钩入其中。从我链接的文档中得知:您还可以提供特殊名称 :memory:,以在 RAM 中创建数据库。 - Nullman
这段代码在pyodbc中可以通过以下方式访问行中的值: row.value1 但在sqlite中必须使用以下方式: row['value1'] 因此,如果我将pyodbc连接替换为sqlite连接,则代码会失败。另一个问题是sqlite不支持datetime对象,这会破坏我的其他测试... - Piotr Siejda
你使用哪种类型的SQL数据库? - Nullman
@Nullman IBM DB2。在您的看法中,pyodbc是错误的选择吗? - Piotr Siejda
1
我不是数据库质量方面的专家,我只是想知道是否可以创建一个临时表来处理数据,就像我在SQLite中建议的那样。结果证明,你是可以这样做的 - Nullman
谢谢!这解决了我的问题 :) 所以答案就是 - "创建临时表"。 - Piotr Siejda
1个回答

5

根据问题中@Nullman的建议,如果您想使用内存数据库,可以尝试使用SQLite ODBC驱动程序,以便您可以返回实际的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中测试过,它对我有效。


1
我这里没有ODBC访问权限,所以无法测试,验证工作做得很好! - Nullman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接