diff --git a/Restful APIs/Testing-RESTful-APIs.md b/Restful APIs/Testing-RESTful-APIs.md new file mode 100644 index 0000000..1cf4c93 --- /dev/null +++ b/Restful APIs/Testing-RESTful-APIs.md @@ -0,0 +1,262 @@ +# **Testing RESTful APIs** + +Testing is an essential part of API development. It ensures that the endpoints behave as expected and helps catch bugs early in the development process. In this section, we’ll cover automated testing using **`pytest`** for **Flask** and **`unittest`** or **`pytest-django`** for **Django**. We’ll also provide a mini-tutorial on how to manually test APIs using **Postman**. + +## **1. Automated Testing in Flask with pytest** + +Testing APIs in Flask is straightforward using **`pytest`**. We’ll start by setting up basic test cases for CRUD operations, and then move on to testing authenticated routes. + +### **Step 1: Install pytest** +```bash +pipenv install pytest pytest-flask +``` + +### **Step 2: Setting Up Test Configuration** +In **`tests/conftest.py`**, create a test configuration to set up a test environment for your Flask app. + +```python +import pytest +from app import create_app, db + +@pytest.fixture +def app(): + app = create_app() + app.config.update({ + "TESTING": True, + "SQLALCHEMY_DATABASE_URI": "sqlite:///:memory:", # Use in-memory database for testing + }) + + with app.app_context(): + db.create_all() # Create the database for tests + yield app # Provide the app to the tests + db.session.remove() + db.drop_all() + +@pytest.fixture +def client(app): + return app.test_client() # Provide a test client +``` +- **`TESTING`**: Enables testing mode in Flask. +- **`SQLALCHEMY_DATABASE_URI`**: Uses an in-memory SQLite database for testing purposes. +- **`client`**: Provides a test client for making requests to the Flask application. + +### **Step 3: Writing Test Cases** +Now, let’s write test cases for CRUD operations and authentication. In **`tests/test_api.py`**, we create tests using pytest. + +```python +def test_create_user(client): + response = client.post('/users', json={ + "name": "John Doe", + "email": "john@example.com", + "age": 30 + }) + assert response.status_code == 201 + data = response.get_json() + assert data['name'] == "John Doe" + +def test_get_users(client): + response = client.get('/users') + assert response.status_code == 200 + data = response.get_json() + assert len(data) > 0 # Ensure we get a list of users + +def test_get_single_user(client): + response = client.get('/users/1') + assert response.status_code == 200 + data = response.get_json() + assert data['id'] == 1 + +def test_update_user(client): + response = client.put('/users/1', json={"name": "Jane Doe"}) + assert response.status_code == 200 + data = response.get_json() + assert data['name'] == "Jane Doe" + +def test_delete_user(client): + response = client.delete('/users/1') + assert response.status_code == 200 +``` +- **Test Case Breakdown**: + - **`test_create_user`**: Tests the creation of a new user via the POST method. + - **`test_get_users`**: Tests retrieving a list of users via the GET method. + - **`test_get_single_user`**: Tests retrieving a specific user by ID. + - **`test_update_user`**: Tests updating a user’s information. + - **`test_delete_user`**: Tests deleting a user. + +### **Step 4: Running the Tests** +To run the tests, simply execute: +```bash +pytest +``` +This will discover and execute all tests within the **`tests/`** directory. + +--- + +## **2. Automated Testing in Django with pytest-django** + +For Django, we can use either the built-in **`unittest`** module or **`pytest-django`** for testing. We’ll cover both approaches, but we recommend using **`pytest-django`** for better flexibility and features. + +### **Option 1: Using pytest-django** + +### **Step 1: Install pytest-django** +```bash +pipenv install pytest pytest-django +``` + +### **Step 2: Configure pytest-django** +In your **`pytest.ini`** or **`conftest.py`** file, configure the test environment: + +```ini +[pytest] +DJANGO_SETTINGS_MODULE = django_crud_api.settings +``` + +### **Step 3: Writing Test Cases** +Create a test file in **`users/tests/test_views.py`**: + +```python +import pytest +from django.urls import reverse +from rest_framework.test import APIClient +from users.models import User + +@pytest.fixture +def api_client(): + return APIClient() + +@pytest.fixture +def create_user(): + return User.objects.create(name="John Doe", email="john@example.com", age=30) + +def test_create_user(api_client): + url = reverse('user-list') # Use Django’s reverse function for the URL + response = api_client.post(url, {"name": "Jane Doe", "email": "jane@example.com", "age": 25}, format='json') + assert response.status_code == 201 + assert response.data['name'] == "Jane Doe" + +def test_get_users(api_client, create_user): + url = reverse('user-list') + response = api_client.get(url) + assert response.status_code == 200 + assert len(response.data) > 0 + +def test_update_user(api_client, create_user): + url = reverse('user-detail', args=[create_user.id]) + response = api_client.put(url, {"name": "John Smith", "email": "johnsmith@example.com"}, format='json') + assert response.status_code == 200 + assert response.data['name'] == "John Smith" + +def test_delete_user(api_client, create_user): + url = reverse('user-detail', args=[create_user.id]) + response = api_client.delete(url) + assert response.status_code == 204 +``` +- **`reverse()`**: Generates the URL for API endpoints. +- **APIClient**: DRF’s test client for making API requests. + +### **Step 4: Running Tests** +To run the tests: +```bash +pytest +``` + +--- + +### **Option 2: Using Django's Unittest Framework** + +If you prefer **`unittest`**, you can write test cases similarly. In **`users/tests.py`**, add: + +```python +from django.test import TestCase +from django.urls import reverse +from users.models import User + +class UserTests(TestCase): + def setUp(self): + self.user = User.objects.create(name="John Doe", email="john@example.com", age=30) + + def test_create_user(self): + response = self.client.post(reverse('user-list'), { + "name": "Jane Doe", "email": "jane@example.com", "age": 25 + }) + self.assertEqual(response.status_code, 201) + self.assertEqual(response.data['name'], "Jane Doe") + + def test_get_users(self): + response = self.client.get(reverse('user-list')) + self.assertEqual(response.status_code, 200) + self.assertGreater(len(response.data), 0) + + def test_update_user(self): + response = self.client.put(reverse('user-detail', args=[self.user.id]), { + "name": "John Smith", "email": "johnsmith@example.com" + }) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data['name'], "John Smith") + + def test_delete_user(self): + response = self.client.delete(reverse('user-detail', args=[self.user.id])) + self.assertEqual(response.status_code, 204) +``` + +Run these tests with: +```bash +python manage.py test +``` + +--- + +## **3. Manual Testing Using Postman** + +**Postman** is a powerful tool that allows you to manually test your RESTful APIs by sending HTTP requests to your server. + +### **Step 1: Setting Up Postman** + +1. **Install Postman**: Download and install Postman from the [official website](https://www.postman.com/downloads/). +2. **Create a New Collection**: Organize your API requests in a collection. A collection helps you manage different endpoints, including GET, POST, PUT, and DELETE requests. + +### **Step 2: Sending Requests** + +Here’s a mini-tutorial to test each CRUD operation manually: + +1. **POST (Create a User)**: + - In Postman, select **POST** and enter the URL: `http://127.0.0.1:5000/users` (or your Django API URL). + - In the **Body** tab, select **raw** and **JSON** format, then enter the following JSON: + ```json + { + "name": "Jane Doe", + "email": "jane@example.com", + "age": 25 + } + ``` + - Click **Send** to create the user. You should receive a **201 Created** response with the new user’s details. + +2. **GET (Retrieve All Users)**: + - Select **GET** and enter `http://127.0.0.1:5000/users`. + - Click **Send**. You should receive a **200 OK** response with a list of users. + +3. **GET (Retrieve a Single User)**: + - Select **GET** and enter `http://127.0.0.1:5000/users/1` (replace `1` with the correct user ID). + - Click **Send**. You + + should receive a **200 OK** response with the user’s details. + +4. **PUT (Update a User)**: + - Select **PUT** and enter `http://127.0.0.1:5000/users/1`. + - In the **Body** tab, enter the updated user details: + ```json + { + "name": "Jane Smith", + "email": "janesmith@example.com" + } + ``` + - Click **Send** to update the user. You should receive a **200 OK** response with the updated details. + +5. **DELETE (Delete a User)**: + - Select **DELETE** and enter `http://127.0.0.1:5000/users/1`. + - Click **Send**. You should receive a **200 OK** response with a confirmation of the deletion. + +## **4. Conclusion** + +- Testing RESTful APIs is critical for ensuring their correctness and reliability. With automated testing in Flask and Django using **`pytest`**, **`unittest`**, or **`pytest-django`**, you can cover a wide range of test cases for your endpoints. Postman provides a manual way to test your APIs and verify responses. +