我想单元测试一个与使用Django Rest Framework构建的API交互的模块。要做到这一点,我正在使用python模拟库。由于我没有使用模拟来测试与API的交互,我遇到了问题。
将对象发送到API端点的功能就是这个(简而言之):
def send_experiment_to_portal(experiment: Experiment):
rest = RestApiClient()
if not rest.active:
return None
# general params
params = {"nes_id": str(experiment.id),
"title": experiment.title,
"description": experiment.description,
"data_acquisition_done":
str(experiment.data_acquisition_is_concluded),
"project_url": experiment.source_code_url,
"ethics_committee_url": experiment.ethics_committee_project_url
}
action_keys = ['experiments', 'create']
portal_experiment = rest.client.action(
rest.schema, action_keys, params=params,
)
return portal_experiment
RestAPIClient
是对API进行身份验证的类:
class RestApiClient(object):
client = None
schema = None
active = False
def __init__(self):
auth = coreapi.auth.BasicAuthentication(
username=settings.PORTAL_API['USER'],
password=settings.PORTAL_API['PASSWORD']
)
self.client = coreapi.Client(auth=auth)
try:
url = settings.PORTAL_API['URL'] + \
(
':' + settings.PORTAL_API['PORT'] if
settings.PORTAL_API['PORT'] else ''
) \
+ '/api/schema/'
self.schema = self.client.get(url)
self.active = True
except:
self.active = False
RestApiClient
类使用coreapi
客户端模块连接到API。
所以,最后,我从测试开始,如果params
中的rest.client.action
参数具有API使用python模拟库预见的键/值。
我开始写初始测试,如:
class PortalAPITest(TestCase):
@patch('experiment.portal.RestApiClient')
def test_send_experiment_to_portal(self, mockRestApiClientClass):
research_project = ObjectsFactory.create_research_project()
experiment = ObjectsFactory.create_experiment(research_project)
result = send_experiment_to_portal(experiment)
因此,当我调试测试时,查看send_experiment_to_portal
函数内部的rest.client.action.call_args
具有以下值:
call(<MagicMock name='RestApiClient().schema' id='139922756141240'>, ['expreiments', 'create'], params={'description': 'Descricao do Experimento-Update', 'ethics_committee_url': None, 'project_url': None, 'title': 'Experimento-Update', 'nes_id': '37', 'data_acquisition_done': 'False'})
但mockRestApiClientClass.client.action.call_args
有None
。我怀疑我做错了。我应该模拟rest.client.action而不是RestApiClient
类。
尝试使用result
值也不起作用。
结果的价值是
<MagicMock name='RestApiClient().client.action()' id='139821809672144'>
和result.call_args
也是None
。
但是怎么做呢?如何在rest
函数之外获得模拟send_experiment_to_portal
实例?
经过几天的挣扎,我找到了解决方案。
模拟RestApiClient
返回类的模拟而不是它的值,即rest
变量的值。我们从模拟对象中获取调用rest
的return_value
值。
因此,我们必须调用mockRestApiClientClass.return_value
而不是mockRestApiClientClass
。
通过这样做,我们可以访问从rest
内部调用的send_experiment_end_message_to_portal
方法和属性,然后mockRestApiClientClass.return_value.client.action.call_args
将具有rest
函数上下文中观察到的send_experiment_end_message_to_portal
内容的相同内容。