您提到 的行为someApiCall
取决于time
参数:
...可以说时间是我关心someApiCall的特定输出的一些价值,我想确保模拟返回...
为此,我们必须拦截对外部someFunction
的调用并检查time
参数,以便我们可以someApiCall
相应地更新。一种解决方案是在someFunction
调用原始.someApiCall
time
someFunction
下面是使用装饰器的实现。我做了两种可能的方法:
- 一个是通过修补
someFunction_decorator_patch
- 另一个通过手动修改源代码实现,然后通过执行重新加载
someFunction_decorator_reload
./src.py
from api import someApiCall
def someFunction(time, a, b, c):
apiReturnA = someApiCall(a)
returnB = b + 1
apiReturnC = someApiCall(c)
return [apiReturnA, returnB, apiReturnC]
./api.py
def someApiCall(var):
return var + 2
./test_src.py
from importlib import reload
import sys
import api
from api import someApiCall
from src import someFunction
import pytest
def amend_someApiCall_yesterday(var):
# Reimplement api.someApiCall
return var * 2
def amend_someApiCall_now(var):
# Reimplement api.someApiCall
return var * 3
def amend_someApiCall_later(var):
# Just wrap around api.someApiCall. Call the original function afterwards. Here we can also put
# some conditionals e.g. only call the original someApiCall if the var is an even number.
var *= 4
return someApiCall(var)
def someFunction_decorator_patch(someFunction, mocker):
def wrapper(time, a, b, c):
# If x imports y.z and we want to patch the calls to z, then we have to patch x.z. Patching
# y.z would still retain the original value of x.z thus still calling the original
# functionality. Thus here, we would be patching src.someApiCall and not api.someApiCall.
if time == "yesterday":
mocker.patch("src.someApiCall", side_effect=amend_someApiCall_yesterday)
elif time == "now":
mocker.patch("src.someApiCall", side_effect=amend_someApiCall_now)
elif time == "later":
mocker.patch("src.someApiCall", side_effect=amend_someApiCall_later)
elif time == "tomorrow":
mocker.patch("src.someApiCall", return_value=0)
else:
# Use the original api.someApiCall
pass
return someFunction(time, a, b, c)
return wrapper
def someFunction_decorator_reload(someFunction):
def wrapper(time, a, b, c):
# If x imports y.z and we want to update the functionality of z, then we have to update
# first the functionality of z then reload x. This way, x would have the updated
# functionality of z.
if time == "yesterday":
api.someApiCall = amend_someApiCall_yesterday
elif time == "now":
api.someApiCall = amend_someApiCall_now
elif time == "later":
api.someApiCall = amend_someApiCall_later
elif time == "tomorrow":
api.someApiCall = lambda var: 0
else:
# Use the original api.someApiCall
api.someApiCall = someApiCall
reload(sys.modules['src'])
return someFunction(time, a, b, c)
return wrapper
@pytest.mark.parametrize(
'time',
[
'yesterday',
'now',
'later',
'tomorrow',
'whenever',
],
)
def test_sample(time, mocker):
a, b, c = 10, 10, 10
someFunction_wrapped_patch = someFunction_decorator_patch(someFunction, mocker)
result_1 = someFunction_wrapped_patch(time, a, b, c)
print("Using patch:", time, result_1)
someFunction_wrapped_reload = someFunction_decorator_reload(someFunction)
result_2 = someFunction_wrapped_reload(time, a, b, c)
print("Using reload:", time, result_2)
输出:
$ pytest -rP
____________________________________________________________________________________ test_sample[yesterday] _____________________________________________________________________________________
------------------------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------------------------
Using patch: yesterday [20, 11, 20]
Using reload: yesterday [20, 11, 20]
_______________________________________________________________________________________ test_sample[now] ________________________________________________________________________________________
------------------------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------------------------
Using patch: now [30, 11, 30]
Using reload: now [30, 11, 30]
______________________________________________________________________________________ test_sample[later] _______________________________________________________________________________________
------------------------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------------------------
Using patch: later [42, 11, 42]
Using reload: later [42, 11, 42]
_____________________________________________________________________________________ test_sample[tomorrow] _____________________________________________________________________________________
------------------------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------------------------
Using patch: tomorrow [0, 11, 0]
Using reload: tomorrow [0, 11, 0]
_____________________________________________________________________________________ test_sample[whenever] _____________________________________________________________________________________
------------------------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------------------------
Using patch: whenever [12, 11, 12]
Using reload: whenever [12, 11, 12]
======================================================================================= 5 passed in 0.03s =======================================================================================
在这里,您可以看到someApiCall
根据time
参数变化的响应。
yesterday
表示 var * 2
now
表示 var * 3
later
表示 (var * 4) + 2
tomorrow
表示 0
- 其他任何东西都意味着 var + 2 的默认实现