I have basic code that I am using to learn how to write Python unittests. It uses the "jokes" API.
The project is very simple and setup like this:
.
├── README.md
├── app
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-39.pyc
│ │ ├── calc.cpython-39.pyc
│ │ ├── joke.cpython-39.pyc
│ │ └── math.cpython-39.pyc
│ ├── calc.py
│ └── joke.py
└── test
├── __init__.py
├── __pycache__
│ ├── __init__.cpython-39.pyc
│ └── test_joke.cpython-39.pyc
├── test_calc.py
└── test_joke.py
the app code:
import requests
'''each time we make the get_joke() , we get a different result
so in order to test the len_joke(), we will mock the get_joke()
the mock will help isolate the len_joke() that is under test,
from its dependency'''
def len_joke():
joke = get_joke()
# add temp code for print testing during test call
return len(joke)
def get_joke():
url = 'http://api.icndb.com/jokes/random'
try:
response = requests.get(url, timeout=30)
response.raise_for_status
except requests.exceptions.Timeout:
return "No jokes!"
except requests.exceptions.ConnectionError:
pass
except requests.exceptions.HTTPError as e:
status_code = e.response.status_code
# return 'HTTP error was raised'
else:
if response.status_code == 200:
joke = response.json()['value']['joke']
else:
joke = "No jokes!"
return joke
In order to test the application code, I have this test code:
import unittest
from unittest.mock import MagicMock, patch
from urllib.error import HTTPError
import requests
from app import joke
from requests.exceptions import Timeout
class TestJoke(unittest.TestCase):
'''each time we make the get_joke() , we get a different result
so in order to test the len_joke(), we will mock the get_joke()
the mock will help isolate the len_joke() that is under test,
from its dependency. Our testing methodology is completely independent
of an external network connection or an external API'''
@patch('app.joke.requests')
def test_get_joke_raise_timeout_exception(self, mock_requests):
mock_requests.exceptions = requests.exceptions
mock_requests.get.side_effect = Timeout('seems like the server is down')
self.assertEqual(joke.get_joke(), 'No jokes!')
# we will again need a "mock response" variable
@patch('app.joke.requests')
def test_get_joke_raise_for_status(self, mock_requests):
mock_requests.exceptions = requests.exceptions
mock_response = MagicMock(status_code=403)
mock_response.raise_for_status.side_effect = HTTPError('Something went wrong')
mock_requests.get.return_value = mock_response
self.assertEqual(joke.get_joke(), 'HTTP error was raised')
The application code is working and I get my joke back from the API. The 'test_get_joke_raise_timeout_exception' test runs fine. However the 'test_get_joke_raise_for_status' is throwing the following error:
mock_response.raise_for_status.side_effect = HTTPError('Something went wrong')
TypeError: __init__() missing 4 required positional arguments: 'code', 'msg', 'hdrs', and 'fp'
Can anyone help me to interpret his traceback and figure out what's wrong with this test?
CodePudding user response:
HTTPError('Something went wrong')
isn't a valid way to instantiate an HTTPError
. The constructor of that class takes mandatory arguments code
, msg
, hdrs
and fp
, so your test will need to supply them.
See the source code and (although it isn't complete, presumably because it expects the class to be used primarily by code in the library, rather than application code) the documentation.