Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support stubbing AppStoreServerAPIClient for unit tests #24

Open
WFT opened this issue Aug 11, 2023 · 2 comments
Open

Support stubbing AppStoreServerAPIClient for unit tests #24

WFT opened this issue Aug 11, 2023 · 2 comments

Comments

@WFT
Copy link
Contributor

WFT commented Aug 11, 2023

Feature request: I'd like the ability to create mock responses to API client calls which will be returned by the next call to a specific client method.

Use case

I would like to do unit testing for my server's use of the library. I want to test the following features of my server:

  1. Customer service requests to extend subscription renewals
  2. Requests to get the latest subscription status of a customer (e.g. for testing my handling of older clients with ReceiptUtility)

Example code

Here's an example test one could write:

from appstoreserverlibrary import testing as appstore

replace_server_api_client(appstore.TestingAPIClient(...))

def test_customer_service_extension(client):
    def callback(original_transaction_id: str, extend_renewal_date_request: ExtendRenewalDateRequest) -> ExtendRenewalDateResponse:
        assert stuff_about_the_request(original_transaction_id, extend_renewal_date_request)
        return ExtendRenewalDateResponse(...)

    with appstore.client_stubber as stubber: # Asserts on leaving the `with` that the expected responses were consumed
        stubber.extend_subscription_renewal_date.add_response(callback)
        response = client.post('/api/customer-service/app-store/extend')
        assert response.code == 200

    # Check that our server is properly handling saving the effectiveDate to the database
    response = client.get('/api/app-store/renewal-date')
    assert response.code == 200
    assert has_been_extended(response)
@alexanderjordanbaker
Copy link
Collaborator

@WFT currently how the unit tests are constructed, either directly or through subclassing you can override the direct method used to make the HTTP call, do any validation required on the inputs, and then return any arbitrary response

def get_client_with_body(self, body: str, expected_method: str, expected_url: str, expected_params: Dict[str, Union[str, List[str]]], expected_json: Dict[str, Any], status_code: int = 200):
signing_key = read_data_from_binary_file('tests/resources/certs/testSigningKey.p8')
client = AppStoreServerAPIClient(signing_key, 'keyId', 'issuerId', 'com.example', Environment.LOCAL_TESTING)
def fake_execute_and_validate_inputs(method: bytes, url: str, params: Dict[str, Union[str, List[str]]], headers: Dict[str, str], json: Dict[str, Any]):
self.assertEqual(expected_method, method)
self.assertEqual(expected_url, url)
self.assertEqual(expected_params, params)
self.assertEqual(['User-Agent', 'Authorization', 'Accept'], list(headers.keys()))
self.assertEqual('application/json', headers['Accept'])
self.assertTrue(headers['User-Agent'].startswith('app-store-server-library/python'))
self.assertTrue(headers['Authorization'].startswith('Bearer '))
decoded_jwt = decode_json_from_signed_date(headers['Authorization'][7:])
self.assertEqual('appstoreconnect-v1', decoded_jwt['payload']['aud'])
self.assertEqual('issuerId', decoded_jwt['payload']['iss'])
self.assertEqual('keyId', decoded_jwt['header']['kid'])
self.assertEqual('com.example', decoded_jwt['payload']['bid'])
self.assertEqual(expected_json, json)
response = Response()
response.status_code = status_code
response.raw = BytesIO(body)
response.headers['Content-Type'] = 'application/json'
return response
client._execute_request = fake_execute_and_validate_inputs
return client
, or if you are looking for a higher level override, _make_request can be overridden which takes the request and response before they are parsed to/from JSON. This could be extended I believe to create the stubbed behavior you are describing

@WFT
Copy link
Contributor Author

WFT commented Nov 21, 2023

@alexanderjordanbaker Yeah I think that makes sense. In my codebase I've just been using a general-purpose HTTP mocking library (https://github.com/gabrielfalcao/HTTPretty -- wish it were a little more maintained). I think that to make a really great testing experience though, it'd be nice if we could get it into this library.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants