I want to unit test a module that interacts with an API built with Django Rest Framework. To do that I'm using python mock library. As I'm not experienced using mocks to test interaction with an API, I'm having problems.
The function that sends an object to an API endpoint is this (in short):
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
The RestAPIClient is the class that authenticates to the 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
The RestApiClient class is using the coreapi client module to connect to the API.
So, at the end, I'm starting from test if the params argument in rest.client.action has the keys/values that are foreseen by the API using python mock library.
I'm starting writing the initial test like:
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)
So when I debug the test, looking inside the send_experiment_to_portal function rest.client.action.call_args has the value:
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'})
But mockRestApiClientClass.client.action.call_args has None. I suspect that I'm making wrong. I should mock rest.client.action and not the RestApiClient class.
Trying to use result value does not work too.
The value of result is
<MagicMock name='RestApiClient().client.action()' id='139821809672144'>
and result.call_args is None too.
But how to do that? How to get the mocking rest instance outside send_experiment_to_portal function?