您有一系列链式调用,每个都返回一个新对象。如果您模拟只是 psycopg2.connect() 调用,您可以通过 .return_value 属性跟踪该调用链(每个都生成模拟对象),这些属性引用此类调用返回的模拟:
@mock.patch("psycopg2.connect")
def test_super_awesome_stuff(self, mock_connect):
expected = [['fake', 'row', 1], ['fake', 'row', 2]]
mock_con = mock_connect.return_value # result of psycopg2.connect(**connection_stuff)
mock_cur = mock_con.cursor.return_value # result of con.cursor(cursor_factory=DictCursor)
mock_cur.fetchall.return_value = expected # return this when calling cur.fetchall()
result = super_cool_method()
self.assertEqual(result, expected)
因为您保留了模拟 connect 函数的引用,以及模拟连接和游标对象,所以您还可以断言它们是否被正确调用:
mock_connect.assert_called_with(**connection_stuff)
mock_con.cursor.called_with(cursor_factory=DictCursor)
mock_cur.execute.called_with("Super duper SQL query")
如果您不需要测试这些,您可以将 return_value 引用链接起来直接访问连接对象上 cursor() 调用的结果:
@mock.patch("psycopg2.connect")
def test_super_awesome_stuff(self, mock_connect):
expected = [['fake', 'row', 1], ['fake', 'row' 2]]
mock_connect.return_value.cursor.return_value.fetchall.return_value = expected
result = super_cool_method()
self.assertEqual(result, expected)
请注意,如果您将连接用作context manager to automatically commit the transaction 并且您使用as 将__enter__() 返回的对象绑定到一个新名称(因此with psycopg2.connect(...) as conn: # ...)然后您'将需要在调用链中注入额外的__enter__.return_value:
mock_con_cm = mock_connect.return_value # result of psycopg2.connect(**connection_stuff)
mock_con = mock_con_cm.__enter__.return_value # object assigned to con in with ... as con
mock_cur = mock_con.cursor.return_value # result of con.cursor(cursor_factory=DictCursor)
mock_cur.fetchall.return_value = expected # return this when calling cur.fetchall()
同样适用于with conn.cursor() as cursor:的结果,conn.cursor.return_value.__enter__.return_value对象被分配给as目标。