Python 3.15.0的unittest.mock为调用记录列出call_args、call_args_list、method_calls和mock_calls。它们都能出现call对象,所保存的集合却不同:一项面向最后一次参数,一项面向对象的调用序列,另两项继续覆盖方法属性链或更广的调用对象。阅读一段记录,需要先保留它来自哪一个字段。[1]
call_args在mock尚未被调用时为None;发生调用后,它保存最后一次调用的参数。位置参数可由args取得,关键字参数可由kwargs取得。官方例中,只有位置参数的调用会得到非空位置参数元组与空关键字字典;空字典在这里表示没有关键字参数,不等于mock从未调用。
无参数调用又提供另一个具体区别。手册将它记录为call(),而未调用时仍是None。前者确实有一次调用,只是参数为空;后者没有这次事件。由此,“没有参数”和“没有调用”不能在说明中都简写成一个空值,也不能凭最后一次记录推出先前是否还出现过其他参数组合。
call_args_list负责保存该mock全部调用,按发生顺序排列,尚无调用时是空列表。文档例先出现无参数调用,再出现位置参数调用,最后出现关键字参数调用,三项各占列表中的一个位置。列表长度对应该对象被调用次数,而call_args只留下其中最后一次的参数信息。
method_calls记录的方法与属性链又有更深的名称。官方先列method(),再列property.method.attribute(),结果清单同时保留这两个名称。后者不是把根mock自身再调用一次的另一种拼写,它指向经属性继续到达的调用位置。这项记录使参数之外的路径身份也留在列表中。
mock_calls覆盖mock自身、它的方法、魔术方法以及返回值mock的调用。手册示例把原对象的一次调用、first和second、__int__,以及所得result的调用依次放在同一清单。这里的长度不能直接套用call_args_list的解释,因为清单中已经包含不同位置、不同类型的调用。
在这份官方例子中,result来自根mock带参数1、2、3的调用;之后result自身又以参数1被调用。清单将前者写为call(1,2,3),后者写为call()(1)。后一项两个括号保留了先取得返回值、再调用它的路径,末尾的1属于返回值mock的这次调用,不是将根对象原来的三个参数覆盖成一个。
嵌套例子最能显示记录范围。原例top(a=3).bottom()产生两项:先是top(a=3),再是top().bottom()。第二项没有再次携带祖先top的参数,所以单独拿这个末项与把祖先参数写成a=-1的形式比较,文档给出相等结果。它没有抹去前一项已保存的a=3,只是说明末项不包含那层参数。
上述条目都能按元组读取参数,但共同的表示形式不会使字段范围相同。本篇依据3.15.0连续定义与纸面示例,未执行测试、patch程序或据这些记录认定真实系统正确;文档更新不充作首次发表日。
信息来源
本文基于上述公开资料整理,未使用来源页面的图片、视频或嵌入媒体。