diff --git a/README.md b/README.md index 2a4b628..d717648 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,23 @@ # Python-API-Testing Testing Python APIs + +## Sections. +- Introduction - Brief intro of the article and setup of the application. +- Test Driven Development(TDD) - A bit of explanation on TDD. +- Utilizing Mocks - Explain the concept of mocking and different ways of mocking as follows. + - Using Decorators + - Using a Context Manager + - Using a Patcher +- Mocking the whole function behavior - Demonstrate and explain how its done. +- Mocking third party functions - Demonstrate and explain how to mock out own functions/modules in other functions. +- ~Continuous integration with Circle CI - Brief walk through of how to combine testing and CI to make our development smooth, use case Github and Circle CI.~ +- Conclusion - Brief summary touching on mocking and CI. + +## Setup. + ```sh +$ git clone git@github.com:kimobrian/Python-API-Testing.git +$ cd Python-API-Testing +$ virtualenv -p python3 venv # Create virtual environment +$ source venv/bin/activate # Activate virtual environment +$ pip install -r requirements.txt + ``` diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/circle.yml b/circle.yml new file mode 100644 index 0000000..1d77dc4 --- /dev/null +++ b/circle.yml @@ -0,0 +1,11 @@ +machine: + python: + version: 3.5.2 + +dependencies: + override: + - pip install -r requirements.txt + +test: + override: + - nose2 --verbose diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..eb97054 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +nose2==0.6.5 +requests==2.18.4 diff --git a/users.py b/users.py new file mode 100644 index 0000000..68c0e37 --- /dev/null +++ b/users.py @@ -0,0 +1,21 @@ +import requests +import json + +USERS_URL = 'http://jsonplaceholder.typicode.com/users' + + +def get_users(): + """Get list of users""" + response = requests.get(USERS_URL) + if response.ok: + return response + else: + return None + + +def get_user(user_id): + """Get a single user using their ID""" + all_users = get_users().json() + for user in all_users: + if user['id'] == user_id: + return user diff --git a/users_test/__init__.py b/users_test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/users_test/test_users.py b/users_test/test_users.py new file mode 100644 index 0000000..0692d0a --- /dev/null +++ b/users_test/test_users.py @@ -0,0 +1,103 @@ +import unittest +from users import get_users, get_user +from unittest.mock import patch, Mock + + +class BasicTests(unittest.TestCase): + @patch('users.requests.get') # Mock 'requests' module 'get' method. + def test_request_response_with_decorator(self, mock_get): + """Mocking using a decorator""" + mock_get.return_value.status_code = 200 # Mock status code of response. + response = get_users() + + # Assert that the request-response cycle completed successfully. + self.assertEqual(response.status_code, 200) + + def test_request_response_with_context_manager(self): + """Mocking using a context manager""" + with patch('users.requests.get') as mock_get: + # Configure the mock to return a response with status code 200. + mock_get.return_value.status_code = 200 + + # Call the function, which will send a request to the server. + response = get_users() + + # Assert that the request-response cycle completed successfully. + self.assertEqual(response.status_code, 200) + + def test_request_response_with_patcher(self): + """Mocking using a patcher""" + mock_get_patcher = patch('users.requests.get') + users = [{ + "id": 0, + "first_name": "Dell", + "last_name": "Norval", + "phone": "994-979-3976" + }] + + # Start patching 'requests.get'. + mock_get = mock_get_patcher.start() + + # Configure the mock to return a response with status code 200. + mock_get.return_value = Mock(status_code=200) + mock_get.return_value.json.return_value = users + + # Call the service, which will send a request to the server. + response = get_users() + + # Stop patching 'requests'. + mock_get_patcher.stop() + + # Assert that the request-response cycle completed successfully. + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), users) + + def test_mock_whole_function(self): + """Mocking a whole function""" + mock_get_patcher = patch('users.requests.get') + users = [{ + "id": 0, + "first_name": "Dell", + "last_name": "Norval", + "phone": "994-979-3976" + }] + + # Start patching 'requests.get'. + mock_get = mock_get_patcher.start() + + # Configure the mock to return a response with status code 200 and a list of users. + mock_get.return_value = Mock(status_code = 200) + mock_get.return_value.json.return_value = users + + # Call the service, which will send a request to the server. + response = get_users() + + # Stop patching 'requests'. + mock_get_patcher.stop() + + # Assert that the request-response cycle completed successfully. + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), users) + + @patch('users.get_users') + def test_get_one_user(self, mock_get_users): + """ + Test for getting one user using their userID + Demonstrates mocking third party functions + """ + users = [ + {'phone': '514-794-6957', 'first_name': 'Brant', + 'last_name': 'Mekhi', 'id': 0}, + {'phone': '772-370-0117', 'first_name': 'Thalia', + 'last_name': 'Kenyatta', 'id': 1}, + {'phone': '176-290-7637', 'first_name': 'Destin', + 'last_name': 'Soledad', 'id': 2} + ] + mock_get_users.return_value = Mock() + mock_get_users.return_value.json.return_value = users + user = get_user(2) + self.assertEqual(user, users[2]) + + +if __name__ == "__main__": + unittest.main()