How can we mock several exception in Pytest, Mock_Response?
Question:
We’re revising our pytest script, according to parent ‘executor’ method revision — added retry logic, to retry 3 times until get response and raise error on 4th.attempt.
Used to be, we test to raise socket error exception as follows, now we need to raise that exception after 3 times retry (below pytest code encountered the test error now).
-- original code
def test_socketerror(monkeypatch, requests_mock):
monkeypatch.setenv('ENV_ID', 'Local')
requests_mock.post("http://localhost:8080/samplejob", exc=socket.error)
with pytest.raises(CommunicationError) as ce:
job_id = 'JOB_ID'
executor = parentjob()
executor.samplejob(job_id, None, None, None, None)
assert str(ce.value.args[0]) == "same error message on parent method"
On the other hand, we can revise the other retry test as expected, to add mock_response with multiple response.
-- other test revise (seems working fine)
def test_retry(monkeypatch, requests_mock):
monkeypatch.setenv('ENV_ID', 'Local')
headers = {'content-type': 'application/json'}
mock_response = [
{'json': {'status': None}, 'status_code': None},
{'json': {'status': None}, 'status_code': None},
{'json': {'status': None}, 'status_code': None},
{'json': {'status': None}, 'status_code': None}
]
requests_mock.post("http://localhost:8080/samplejob", headers=headers, json=mock_response)
with pytest.raises(CommunicationError) as ce:
job_id = 'JOB_ID'
executor = parentjob()
executor.samplejob(job_id, None, None, None, None)
assert str(ce.value.args[0]) == "same error message on parent method"
But for Socket Error exception, can’t apply multiple mock_response.
Let me also add the parent job:
class parent
def parentjob(self, job_id, date, subject, fromDate, toDate):
for error_count in range(self.error_retry_times):
try:
request_url = 'http://' + self.hostname + '/samplejob'
headers = {'content-type': 'application/json'}
response = requests.post(request_url, data=json.dumps(payload), timeout=self.timeout, headers=headers)
except Timeout as te:
error_message = 'access batch api timeout.jobId=%s, count=%d' % (job_id, error_count)
except Exception as e:
error_message = 'failed to access batch api.jobId=%s, count=%d' % (job_id, error_count)
if response is None:
time.sleep(self.error_retry_inerval)
else:
break
if response is None or response.status_code != 202:
error_message = "error message on parent method"
raise CommunicationError(error_message)
return response
Answers:
You can use mocker fixture like so:
def test_retry_exception(mocker):
mocker.patch("parent_module_path.requests.post", side_effect = [Exception("first time"), Exception("second time"), Exception("third time"), Exception("forth time")])
with pytest.raises(CommunicationError) as ce:
job_id = 'JOB_ID'
executor = parentjob()
executor.samplejob(job_id, None, None, None, None)
assert str(ce.value.args[0]) == "same error message on parent method"
Thanks to the great hint of @Peter K, I successed to raise and test multiple exceptions in requests mock like this;
def test_socketerror(monkeypatch, requests_mock):
monkeypatch.setenv('ENV_ID', 'Local')
***requests_mock.side_effect = (socket.error(),socket.error(),socket.error(),socket.error())
requests_mock.post("http://localhost:8080/samplejob")***
with pytest.raises(CommunicationError) as ce:
job_id = 'JOB_ID'
executor = parentjob()
executor.samplejob(job_id, None, None, None, None)
assert str(ce.value.args[0]) == "same error message on parent method"
Also, it can be applied for timeout error;
requests_mock.side_effect = (requests.exceptions.Timeout(),requests.exceptions.Timeout(),requests.exceptions.Timeout(),requests.exceptions.Timeout())
Hopefully, it can be little help for someone who has same issue in mock usage.
We’re revising our pytest script, according to parent ‘executor’ method revision — added retry logic, to retry 3 times until get response and raise error on 4th.attempt.
Used to be, we test to raise socket error exception as follows, now we need to raise that exception after 3 times retry (below pytest code encountered the test error now).
-- original code
def test_socketerror(monkeypatch, requests_mock):
monkeypatch.setenv('ENV_ID', 'Local')
requests_mock.post("http://localhost:8080/samplejob", exc=socket.error)
with pytest.raises(CommunicationError) as ce:
job_id = 'JOB_ID'
executor = parentjob()
executor.samplejob(job_id, None, None, None, None)
assert str(ce.value.args[0]) == "same error message on parent method"
On the other hand, we can revise the other retry test as expected, to add mock_response with multiple response.
-- other test revise (seems working fine)
def test_retry(monkeypatch, requests_mock):
monkeypatch.setenv('ENV_ID', 'Local')
headers = {'content-type': 'application/json'}
mock_response = [
{'json': {'status': None}, 'status_code': None},
{'json': {'status': None}, 'status_code': None},
{'json': {'status': None}, 'status_code': None},
{'json': {'status': None}, 'status_code': None}
]
requests_mock.post("http://localhost:8080/samplejob", headers=headers, json=mock_response)
with pytest.raises(CommunicationError) as ce:
job_id = 'JOB_ID'
executor = parentjob()
executor.samplejob(job_id, None, None, None, None)
assert str(ce.value.args[0]) == "same error message on parent method"
But for Socket Error exception, can’t apply multiple mock_response.
Let me also add the parent job:
class parent
def parentjob(self, job_id, date, subject, fromDate, toDate):
for error_count in range(self.error_retry_times):
try:
request_url = 'http://' + self.hostname + '/samplejob'
headers = {'content-type': 'application/json'}
response = requests.post(request_url, data=json.dumps(payload), timeout=self.timeout, headers=headers)
except Timeout as te:
error_message = 'access batch api timeout.jobId=%s, count=%d' % (job_id, error_count)
except Exception as e:
error_message = 'failed to access batch api.jobId=%s, count=%d' % (job_id, error_count)
if response is None:
time.sleep(self.error_retry_inerval)
else:
break
if response is None or response.status_code != 202:
error_message = "error message on parent method"
raise CommunicationError(error_message)
return response
You can use mocker fixture like so:
def test_retry_exception(mocker):
mocker.patch("parent_module_path.requests.post", side_effect = [Exception("first time"), Exception("second time"), Exception("third time"), Exception("forth time")])
with pytest.raises(CommunicationError) as ce:
job_id = 'JOB_ID'
executor = parentjob()
executor.samplejob(job_id, None, None, None, None)
assert str(ce.value.args[0]) == "same error message on parent method"
Thanks to the great hint of @Peter K, I successed to raise and test multiple exceptions in requests mock like this;
def test_socketerror(monkeypatch, requests_mock):
monkeypatch.setenv('ENV_ID', 'Local')
***requests_mock.side_effect = (socket.error(),socket.error(),socket.error(),socket.error())
requests_mock.post("http://localhost:8080/samplejob")***
with pytest.raises(CommunicationError) as ce:
job_id = 'JOB_ID'
executor = parentjob()
executor.samplejob(job_id, None, None, None, None)
assert str(ce.value.args[0]) == "same error message on parent method"
Also, it can be applied for timeout error;
requests_mock.side_effect = (requests.exceptions.Timeout(),requests.exceptions.Timeout(),requests.exceptions.Timeout(),requests.exceptions.Timeout())
Hopefully, it can be little help for someone who has same issue in mock usage.