You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I'm looking for help with a better workaround or solution for handling mocking with classes that inherit from ConfigurableResource. Here's the issue I'm running into with an example.
Say I have this class, a very simple API client class that's a ConfigurableResource and handles setting a bearer token value for paginated API calls:
class RestApiClient(ConfigurableResource):
_api_token: Optional[ApiToken] = None
@property
def api_token(self) -> ApiToken:
if not self._api_token or self._api_token.has_expired():
self._api_token = self._refresh_access_token()
return self._api_token
def refresh_access_token(self) -> ApiToken:
# Code for handling a POST call for an API token value
def get_paginated(self, api_path: str) -> List[Dict]:
headers = {}
headers["Authorization"] = "Bearer {}".format(self.api_token.value)
response = requests.Session().get(request_url, headers=headers)
response.raise_for_status()
# code for handling response data and paginated calls, etc.
If I want to test that calling get_paginated means refresh_access_token is called, I would write a unit test like this, where I patch the object to mock a method so I can make assertions on call behavior:
@pytest.fixture
def rest_api_client():
return RestApiClient(...)
def test_calls_refresh_access_token_if_no_access_token(rest_api_client, mock_response):
# After initial construction, no api_token should be present so we expect a call to refresh_access_token
with (
patch('code_location.resources.rest_api.requests.Session.get') as mock_get,
patch.object(rest_api_client, 'refresh_access_token') as mock_refresh_access_token
):
mock_get.return_value = mock_response
rest_api_client.get_paginated(FAKE_API_ENDPOINT)
mock_refresh_access_token.assert_called_once()
The problem is, this test will fail. It fails because the way unittest.mock.patch apparently handles mocking is by using assignment to make a method a Mock object (which records interactions so we can call methods like .assert_called_once()). But ConfigurableResource objects are Pydantic BaseModels with Frozen=True which don't allow re-assignment like this, so the mock fails and the test fails with a Pydantic ValidationError.
One workaround for this is to autospec a MagicMock object with the ApiClient class like MagicMock(spec=RestApiClient). But this object does not have the same method definitions so the mocked version of get_paginated does not call refresh_access_token, and so I can't assert the method was called (unless I redefine mock methods for the MagicMock which defeats the purpose of the test).
Currently, I just don't write those kinds of unit tests for ConfigurableResource classes, but I feel like this limitation around mocking for ConfigurableResources limits test coverage. Am I missing a better way to unit test ConfigurableResources or a better workaround?
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
Hi,
I'm looking for help with a better workaround or solution for handling mocking with classes that inherit from ConfigurableResource. Here's the issue I'm running into with an example.
Say I have this class, a very simple API client class that's a ConfigurableResource and handles setting a bearer token value for paginated API calls:
If I want to test that calling
get_paginated
meansrefresh_access_token
is called, I would write a unit test like this, where I patch the object to mock a method so I can make assertions on call behavior:The problem is, this test will fail. It fails because the way
unittest.mock.patch
apparently handles mocking is by using assignment to make a method a Mock object (which records interactions so we can call methods like .assert_called_once()). But ConfigurableResource objects are Pydantic BaseModels withFrozen=True
which don't allow re-assignment like this, so the mock fails and the test fails with a Pydantic ValidationError.One workaround for this is to autospec a MagicMock object with the ApiClient class like
MagicMock(spec=RestApiClient)
. But this object does not have the same method definitions so the mocked version ofget_paginated
does not callrefresh_access_token
, and so I can't assert the method was called (unless I redefine mock methods for the MagicMock which defeats the purpose of the test).Currently, I just don't write those kinds of unit tests for ConfigurableResource classes, but I feel like this limitation around mocking for ConfigurableResources limits test coverage. Am I missing a better way to unit test ConfigurableResources or a better workaround?
Beta Was this translation helpful? Give feedback.
All reactions