""" Mock the `requests.get` function, and handle collecting data to do so. Three modes are available, and controlled via the `PUBS_TESTS_MODE` environment variable. To modify the variable, under linux or macos, do one of: $ export PUBS_TESTS_MODE=MOCK $ export PUBS_TESTS_MODE=COLLECT $ export PUBS_TESTS_MODE=ONLINE The MOCK mode is the default one, active even if PUBS_TESTS_MODE has not been set. It uses saved data to run pubs units tests relying on the `requests.get` function without the need of an internet connection (it is also much faster). The prefected data is save in the `test_apis_data.pickle` file. The COLLECT mode does real GET requests, and updates the `test_apis_data.pickle` file. It is needed if you add or modify the test relying on `requests.get`. The ONLINE mode bypasses all this and use the original `requests.get` without accessing or updating the `test_apis_data.pickle` data. It might be useful when running tests on Travis for instance. """ import os import json import mock import requests _orgininal_requests_get = requests.get _collected_responses = [] _data_filepath = os.path.join(os.path.dirname(__file__), 'test_apis_data.json') class MockingResponse: def __init__(self, text, status_code=200, error_msg=None): self.text = text self.status_code = status_code self.error_msg = error_msg self.encoding = 'utf8' def raise_for_status(self): if self.status_code != 200: raise requests.exceptions.RequestException(self.error_msg) def intercept_text(text): try: if '10.1103/PhysRevD.89.084044' in text: # replace with wrong DOI text = text.replace('PhysRevD', 'INVALIDDOI') except TypeError: if b'10.1103/PhysRevD.89.084044' in text: # replace with wrong DOI text = text.replace(b'PhysRevD', b'INVALIDDOI') return text mode = os.environ.get('PUBS_TESTS_MODE', 'MOCK') if mode == 'MOCK': with open(os.path.join(_data_filepath), 'r') as fd: _collected_responses = json.load(fd) def mock_requests_get(*args, **kwargs): for args2, kwargs2, text, status_code, error_msg in _collected_responses: if list(args) == list(args2) and kwargs == kwargs2: return MockingResponse(text, status_code, error_msg) raise KeyError(('No stub data found for requests.get({}, {}).\n You may' ' need to update the mock data. Look at the ' 'tests/mock_requests.py file for an explanation').format(args, kwargs)) elif mode == 'COLLECT': def mock_requests_get(*args, **kwargs): text, status_code, error_msg = None, None, None try: r = _orgininal_requests_get(*args, **kwargs) text, status_code = r.text, r.status_code r.raise_for_status() except requests.exceptions.RequestException as e: error_msg = str(e) text = intercept_text(text) _collected_responses.append((args, kwargs, text, status_code, error_msg)) _save_collected_responses() # yes, we save everytime, because it's not # clear how to run once after all the tests # have run. If you figure it out... return MockingResponse(text, status_code, error_msg) def _save_collected_responses(): with open(os.path.join(_data_filepath), 'w') as fd: json.dump(sorted(_collected_responses), fd, indent=2) elif mode == 'ONLINE': def mock_requests_get(*args, **kwargs): # with mock.patch('requests.Response.text', new_callable=mock.PropertyMock) as mock_text: r = _orgininal_requests_get(*args, **kwargs) r._content = intercept_text(r.content) # print(r.content.__class__) # mock_text.return_value = intercept_text(r.text) return r