Pythonic方式来模拟pyodbc.Row

时间:2017-07-26 12:04:59

标签: python unit-testing pyodbc

根据pyodbc进行的SQL查询,对函数进行正确单元测试的pythonic方法是什么?据我所知,最好的方法是模拟从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)

我唯一担心的是,mock返回的是namedtuple而不是像原始函数一样的pyodbc.Row。我检查了以下网站,以搜索有关如何正确创建pyodbc.Row的信息:

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

  

然而,有一些pyodbc添加使它们非常方便:

     
      
  • 可以按列名访问值。

  •   
  • 即使在光标关闭后也可以访问Cursor.description值。

  •   
  • 可以替换值。

  •   
  • 来自同一选择语句的行共享内存。

  •   

因此,它确实指出,namedtuple实际上与pyodbc.Row的行为方式相同(当涉及访问值时)。是否有更pythonic方式在pyodbc.Row上进行单元测试?可以假设这是一个很好的模拟?

1 个答案:

答案 0 :(得分:1)

除了来自@Nullman对该问题的评论的建议之外,如果您想使用内存数据库,您可以尝试使用SQLite ODBC驱动程序,这样您就可以返回实际的pyodbc.Row对象,如下所示: / p>

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中为我工作。