Often the web framework that you use to implement the REST api will also offer unit testing support. For example:
- Flask: http://flask.pocoo.org/docs/latest/testing/
- Django: http://django-testing-docs.readthedocs.org/en/latest/views.html
These test classes are shortcuts which plug the request directly into the framework's Url dispatcher. That saves you the hassle of finding a free port, spawning a "real" server and connecting the http client from your unit test.
As for the e-mail sending: I would mock that part in the TestCase.setUp method. Just change the reference to the e-mail sending module / class to another module/class which loops the outgoing e-mail back to the unit test for evaluation rather than e-mailing.
Videos
There are several unit test frameworks available in Python. Try/except blocks are good for error handling, but you still need a separate unit test around the call if you want to unit test it.
You do have something you can test, you can just return it and test that in your unit test.
Example Unit test using unittest:
import unittest
import requests
class RestCalls():
def google_do_something(blahblah):
url= blahblah
try:
r = requests.get(url,timeout=1)
r.raise_for_status()
return r.status_code
except requests.exceptions.Timeout as errt:
print (errt)
raise
except requests.exceptions.HTTPError as errh:
print (errh)
raise
except requests.exceptions.ConnectionError as errc:
print (errc)
raise
except requests.exceptions.RequestException as err:
print (err)
raise
class TestRESTMethods(unittest.TestCase):
def test_valid_url(self):
self.assertEqual(200,RestCalls.google_do_something('http://www.google.com/search'))
def test_exception(self):
self.assertRaises(requests.exceptions.Timeout,RestCalls.google_do_something,'http://localhost:28989')
if __name__ == '__main__':
unittest.main()
Executing should show (made some edits to this post, updated output included at bottom of post):
> python .\Tests.py
.
----------------------------------------------------------------------
Ran 1 test in 0.192s
OK
If you asserted a different response code from your request, it would fail (the request is just returning http response codes):
python .\Tests.py
F
======================================================================
FAIL: test_upper (__main__.TestStringMethods)
----------------------------------------------------------------------
Traceback (most recent call last):
File ".\Tests.py", line 25, in test_upper
self.assertEqual(404,RestCalls.google_do_something('search'))
AssertionError: 404 != 200
----------------------------------------------------------------------
Ran 1 test in 0.245s
FAILED (failures=1)
Which is expected.
Edit: Included exception testing. You can test these by just including raise in the except block, which will show this after running:
> python .\Tests.py
HTTPConnectionPool(host='localhost', port=28989): Max retries exceeded with url: / (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x03688598>, 'Connection to localhost timed out. (connect timeout=1)'))
..
----------------------------------------------------------------------
Ran 2 tests in 2.216s
OK
References:
- Unit tests in Python
- https://docs.python.org/3/library/unittest.html
- https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
I am not sure that your approach is such a good idea (just printing something in case of an error) but you could mock the print function to see if it was really called (and with what arguments):
https://docs.python.org/3/library/unittest.mock.html?highlight=mock#module-unittest.mock
Edit:
Working with mocks is a bit tricky as far as I remember. You would have to mock the print function in the current module. Perhaps something like this (not tested ...):
from unittest.mock import patch
from unittest import TestCase
class TestGoogleDoSomething(TestCase)
@patch("nameOfYourModule.print")
def test_google_do_something(self, print_mock): # the decorator will pass the mock object into the function
g = google_do_something('blahblah')
print_mock.assert_called_with("your error message here ...")
Restish has a built in TestApp class that can be used to test restish apps. Assuming you have a "test" dir in your root restish project callte "restest" created with paster.
import os
import unittest
from paste.fixture import TestApp
class RootTest (unittest.TestCase):
def setUp(self):
self.app = TestApp('config:%s/../development.ini' % os.path.dirname(os.path.abspath(__file__)))
def tearDown(self):
self.app = None
def test_html(self):
res = self.app.get('/')
res.mustcontain('Hello from restest!')
if __name__ == '__main__':
unittest.main()
I test everything using WebTest and NoseTests and I can strongly recommend it. It's fast, flexible and easy to set up. Just pass it your wsgi function and you're good to go.