简体   繁体   中英

Writing a unit test for Python REST API function

I'm currently learning Python REST API (side project). I've been reading a lot of tutorials from RealPython, Python Requests documentation, etc. I found this post on how to write try/except properly in Python ( Correct way to try/except using Python requests module? ). One thing that still confuses me though is how to create a unit test for a function like this since it is not returning anything. Any help?

def google_do_something(blahblah):
    url='http://www.google.com/' + blahblah
    try:
        r = requests.get(url,timeout=3)
        r.raise_for_status()
    except requests.exceptions.HTTPError as errh:
        print (errh)
    except requests.exceptions.ConnectionError as errc:
        print (errc)
    except requests.exceptions.Timeout as errt:
        print (errt)
    except requests.exceptions.RequestException as err:
        print (err)

I could think of this but I don't know what to assert with.

def test_google_do_something():
    g = google_do_something('blahblah')
    # assert??

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 ...")

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:

It seems that you are using print instead of all exception handlers. I don't think that is a good practice. From my perspective, I prefer to raise those Exceptions out again if not sure how to deal with them right now.

With that said, when any error occurs, an exception will be thrown out; if there're no exceptions, that means this function work well. Therefore you can design your unit test cases base on that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM