如何使用Python Mock断言含列表参数的调用并验证列表首元素
解决Python Mock中列表参数的部分匹配问题
你的问题很常见——当需要断言函数调用时,只关心列表参数的第一个元素,而不在乎列表的长度和后续元素,直接用[2, ANY]会因为列表长度不匹配导致断言失败。不用手动遍历calls,Mock库(不管是Python2的mock还是Python3标准库的unittest.mock)其实有更简洁的内置方法来实现这个需求。
方法一:直接检查call_args(最简单)
你可以通过mock_something.call_args直接获取调用时的参数,然后针对性地验证列表的第一个元素:
import unittest from mock import patch, ANY # Python2用这个;Python3替换为from unittest.mock import patch, ANY class foo(): def something(self, a, b): print(a, b) class footest(unittest.TestCase): def testany(self): f = foo() with patch.object(f, "something", autospec=True) as mock_something: f.something(1, [2,3,4]) # 先断言函数被调用(可选,确保调用次数正确) mock_something.assert_called_once() # 获取调用的位置参数 args, _ = mock_something.call_args # 验证第二个参数(列表)的第一个元素是2 self.assertEqual(args[1][0], 2) if __name__ == '__main__': unittest.main()
方法二:使用自定义匹配器(更灵活)
如果需要更通用的列表部分匹配逻辑,可以自定义一个匹配器类,让它在断言时只检查列表的指定位置元素:
import unittest from mock import patch, ANY, Mock # Python3替换为unittest.mock class ListStartsWith(Mock): def __init__(self, first_element): self.first_element = first_element def __eq__(self, other): # 检查other是否是列表,且第一个元素匹配 return isinstance(other, list) and other[0] == self.first_element class foo(): def something(self, a, b): print(a, b) class footest(unittest.TestCase): def testany(self): f = foo() with patch.object(f, "something", autospec=True) as mock_something: f.something(1, [2,3,4]) # 使用自定义匹配器断言 mock_something.assert_called_with(ANY, ListStartsWith(2)) if __name__ == '__main__': unittest.main()
这个方法的好处是可以复用匹配逻辑,比如你需要检查列表前N个元素时,只需要扩展__eq__方法即可。
为什么原来的代码会失败?
你原来用的mock_something.assert_called_with(ANY, [2, ANY])是严格匹配列表的结构:它期望第二个参数是一个包含两个元素的列表,第一个是2,第二个是任意值。但你实际传入的是三个元素的列表[2,3,4],所以结构不匹配,断言自然失败。
内容的提问来源于stack exchange,提问作者smbaker
相关产品推荐
相关产品推荐

