问题背景
在 Python 中测试函数调用顺序是一个常见的需求。例如,您可能有一个对象 Obj,其中包含三个方法:method1、method2 和 method3。您还编写了一个函数 do_something,该函数调用这些方法。您想编写一个测试来测试 do_something 函数和 Obj 对象。但是,您不想直接模拟或改变 Obj 对象的行为。您需要一种方法来获取在 obj 对象上调用的方法列表,而无需更改其行为。
解决方案
方法一:使用 trace 包
您可以使用 trace 包来获取在 obj 对象上调用的方法列表。trace 包是一个 Python 内置的调试工具,它允许您跟踪函数的调用和返回。
要使用 trace 包,您需要先安装它。您可以使用以下命令安装 trace 包:
pip install trace
安装完成后,您就可以使用 trace 包了。以下是如何使用 trace 包来获取在 obj 对象上调用的方法列表:
import sys
import trace
# 创建一个 Trace 对象,告诉它要忽略哪些目录,以及是要进行跟踪还是行计数。
tracer = trace.Trace(
ignoredirs=[sys.prefix, sys.exec_prefix],
trace=0,
count=1)
# 使用给定的跟踪器运行新命令。
tracer.run('main()')
# 生成报告,并将输出放在当前目录中。
r = tracer.results()
r.write_results(show_missing=True, coverdir=".")
trace 包会生成一个报告,其中包含了在 obj 对象上调用的方法列表。您可以使用这个报告来测试 do_something 函数和 Obj 对象。
方法二:使用 Wrapper 类
您还可以创建一个通用的 Wrapper 类来封装您的对象并跟踪对它的更改。Wrapper 类会拦截对对象的所有属性访问并将其记录下来。
以下是如何编写一个 Wrapper 类:
class Obj:
def method1(self):
print 'method1'
def method2(self):
print 'method2'
def method3(self):
print 'method3'
class Wrapper:
def __init__(self, wrapped):
self.calls = []
self._wrapped = wrapped
def __getattr__(self, n):
self.calls.append(n)
return getattr(self._wrapped, n)
要使用 Wrapper 类,您可以像这样:
obj = Obj()
x = Wrapper(obj)
x.method2()
x.method1()
x.method3()
print(x.calls)
输出:
['method2', 'method1', 'method3']
您可以使用 Wrapper 类来测试 do_something 函数和 Obj 对象。
代码例子
以下是如何使用 trace 包和 Wrapper 类来测试 do_something 函数和 Obj 对象:
使用 trace 包
import sys
import trace
class Obj:
def method1(self):
print 'method1'
def method2(self):
print 'method2'
def method3(self):
print 'method3'
def do_something():
obj = Obj()
obj.method2()
obj.method1()
obj.method3()
# 创建一个 Trace 对象,告诉它要忽略哪些目录,以及是要进行跟踪还是行计数。
tracer = trace.Trace(
ignoredirs=[sys.prefix, sys.exec_prefix],
trace=0,
count=1)
# 使用给定的跟踪器运行新命令。
tracer.run('do_something()')
# 生成报告,并将输出放在当前目录中。
r = tracer.results()
r.write_results(show_missing=True, coverdir=".")
使用 Wrapper 类
class Obj:
def method1(self):
print 'method1'
def method2(self):
print 'method2'
def method3(self):
print 'method3'
class Wrapper:
def __init__(self, wrapped):
self.calls = []
self._wrapped = wrapped
def __getattr__(self, n):
self.calls.append(n)
return getattr(self._wrapped, n)
def do_something():
obj = Obj()
obj.method2()
obj.method1()
obj.method3()
# 创建一个 Wrapper 对象,并用它来封装 Obj 对象。
x = Wrapper(obj)
# 调用 do_something() 函数。
do_something()
# 打印出在 Obj 对象上调用的方法列表。
print(x.calls)
输出:
['method2', 'method1', 'method3']