Python3.8下如何让Squish的test方法报告原始调用位置而非Wrapper模块位置
Python3.8下如何让Squish的test方法报告原始调用位置而非Wrapper模块位置
这个场景我太熟悉了——自己写了wrapper给Squish的测试方法加额外逻辑,结果报错全指向wrapper,根本找不到哪行测试代码出了问题,头疼得很!好在Python的栈帧操作能解决这个问题,核心就是让Squish的test.*方法以为自己是被原始测试代码直接调用的,跳过咱们的wrapper这一层。
针对Python3.8,给你一个实用的实现方案:
我们可以利用CPython的sys._getframe() API来临时修改调用栈,让Squish的方法“看”到原始调用位置。具体代码如下:
import sys def my_verify(condition, error_msg="Verification failed"): # 先执行你自己的额外逻辑,比如截图、日志记录之类的 # ... 你的自定义代码 ... # 获取原始调用者的栈帧(跳过当前wrapper函数这一层) caller_frame = sys._getframe(1) # 保存当前wrapper栈帧的原始调用者引用,后面要恢复 original_back = sys._getframe().f_back try: # 临时修改wrapper的栈帧,让它指向原始调用者 sys._getframe().f_back = caller_frame # 调用Squish的test.verify,这时候它会以为是原始位置调用的 return test.verify(condition, error_msg) finally: # 必须恢复原始的栈帧引用,避免影响其他代码的调用栈 sys._getframe().f_back = original_back
原理很简单:Squish的test.verify这类方法内部会通过调用栈来定位报错位置,我们在wrapper里临时把调用栈的“上一层”改成原始测试代码的位置,等调用完Squish的方法再恢复回去,这样既不影响wrapper的额外逻辑,又能让Squish UI准确报错到测试代码的行号。
需要注意的是,sys._getframe()是CPython的私有API,但在Python3.8里它的行为非常稳定,Squish本身也是基于CPython运行的,所以完全可以放心用。
如果你的wrapper不止一个(比如还要包装test.compare),可以把这个栈帧操作抽成一个装饰器,这样所有wrapper都能复用,代码更整洁:
import sys from functools import wraps def preserve_call_site(func): @wraps(func) def wrapper(*args, **kwargs): caller_frame = sys._getframe(1) original_back = sys._getframe().f_back try: sys._getframe().f_back = caller_frame return func(*args, **kwargs) finally: sys._getframe().f_back = original_back return wrapper # 用装饰器包装你的Squish方法调用 @preserve_call_site def my_verify(condition, msg=""): # 你的额外逻辑 return test.verify(condition, msg) @preserve_call_site def my_compare(a, b, msg=""): # 你的额外逻辑 return test.compare(a, b, msg)
这样不管你包装多少个Squish的测试方法,都能轻松让报错位置指向原始调用处啦!
备注:内容来源于stack exchange,提问作者Karoly Horvath
相关产品推荐
相关产品推荐

