Showing posts with label unittest. Show all posts
Showing posts with label unittest. Show all posts

Monday

Python Code Testing

 Let's look at how to test our Python code and follow the code coverage as much as possible.


  1. How to follow the MVC pattern in FastAPI

  2. How to write Pythonic code

  3. Types of testing with pytest

  4. Usage of patching, monkeypatching, fixture, and mocking


๐Ÿš€ How to Follow the MVC Pattern in FastAPI

FastAPI doesn’t enforce a strict MVC structure, but you can follow an organized MVC-like structure:

๐Ÿ”น MVC Directory Structure Example

app/
│
├── models/           # ORM models (e.g., SQLAlchemy)
│   └── user.py
│
├── schemas/          # Pydantic schemas (DTOs)
│   └── user.py
│
├── controllers/      # Business logic (aka services)
│   └── user_controller.py
│
├── routes/           # Route definitions
│   └── user_routes.py
│
├── main.py           # Entry point
└── database.py       # DB engine/session

๐Ÿ”น MVC Mapping

  • Modelapp/models/

  • Viewapp/routes/ (FastAPI endpoints)

  • Controllerapp/controllers/ (business logic)


๐Ÿ How to Write Pythonic Code

Follow these tips for clean, maintainable code:

✅ Do's

  • Use list comprehensions: squares = [x**2 for x in range(10)]

  • Use unpacking: a, b = b, a

  • Use f-strings: f"Hello, {name}!"

  • Follow PEP8: spacing, naming conventions

  • Write modular code using functions and classes

❌ Don'ts

  • Avoid deeply nested code

  • Avoid hardcoded values (use config files or env vars)

  • Avoid long functions (>40 lines ideally)


๐Ÿงช Code Coverage & Testing with pytest

๐Ÿ”น Types of Tests

  • Unit Tests: Test isolated functions/methods

  • Integration Tests: Test multiple modules working together

  • Functional Tests: Test end-to-end use cases

  • Regression Tests: Ensure new code doesn't break existing functionality

๐Ÿ”น Measuring Code Coverage

pip install pytest pytest-cov
pytest --cov=app tests/

๐Ÿงฐ Testing Utilities in pytest

1. Fixtures

  • Provide pre-loaded data or setup

import pytest

@pytest.fixture
def user_data():
    return {"username": "test", "email": "test@test.com"}

2. Mocking

  • Replace parts of the system to isolate the test

from unittest.mock import Mock

mock_service = Mock()
mock_service.get_user.return_value = {"name": "Mocked"}

3. Patching

  • Temporarily replace object/function for the test

from unittest.mock import patch

@patch("app.controllers.user_controller.get_user")
def test_get_user(mock_get_user):
    mock_get_user.return_value = {"name": "Patched"}

4. Monkeypatching

  • Provided by pytest to change attributes during test

def test_env(monkeypatch):
    monkeypatch.setenv("API_KEY", "test123")

๐Ÿ“Œ Summary

  • Use a clean MVC layout in FastAPI

  • Write Pythonic code using idioms and best practices

  • Use pytest with fixtures, mocking, patching, and monkeypatching for robust test coverage

  • Measure test coverage with pytest-cov


Here’s how you can quickly start with pytest, pytest-cov, mocking, and code coverage for your FastAPI MVC app:


๐Ÿš€ Quick Setup Commands

# Step 1: Install required packages
pip install pytest pytest-cov pytest-mock

# Step 2: Run all tests
pytest

# Step 3: Run tests with coverage
pytest --cov=app --cov-report=term-missing

# Optional: For HTML coverage report
pytest --cov=app --cov-report=html

๐Ÿ”— Official Docs for Reference


Some code example to understand in better way

๐Ÿงญ FastAPI MVC Pattern and Testing Guide with pytest, Mocking, and Integration Tests (No Real DB)


๐Ÿ“ Folder Structure (MVC + Testing)

project/
├── app/
│   ├── controllers/
│   │   └── user_controller.py
│   ├── models/
│   │   └── user.py
│   ├── schemas/
│   │   └── user.py
│   ├── routes/
│   │   └── user_routes.py
│   ├── database.py
│   └── main.py
├── tests/
│   ├── controllers/
│   │   └── test_user_controller.py
│   ├── routes/
│   │   └── test_user_routes.py
│   ├── conftest.py
│   └── integration/
│       └── test_app.py

1️⃣ app/schemas/user.py

from pydantic import BaseModel

class UserCreate(BaseModel):
    name: str
    email: str

class UserOut(BaseModel):
    id: int
    name: str
    email: str

2️⃣ app/models/user.py

from pydantic import BaseModel

# Simulating a DB model with static data
class User(BaseModel):
    id: int
    name: str
    email: str

    @staticmethod
    def get_users():
        return [
            User(id=1, name="Alice", email="alice@test.com"),
            User(id=2, name="Bob", email="bob@test.com")
        ]

3️⃣ app/controllers/user_controller.py

from app.models.user import User

def list_users():
    return User.get_users()

4️⃣ app/routes/user_routes.py

from fastapi import APIRouter
from app.controllers.user_controller import list_users
from app.schemas.user import UserOut
from typing import List

router = APIRouter()

@router.get("/users", response_model=List[UserOut])
def get_users():
    return list_users()

5️⃣ app/main.py

from fastapi import FastAPI
from app.routes import user_routes

app = FastAPI()
app.include_router(user_routes.router)

✅ Unit Testing with pytest

๐Ÿ“Œ tests/controllers/test_user_controller.py

from app.controllers.user_controller import list_users

def test_list_users():
    users = list_users()
    assert len(users) == 2
    assert users[0].name == "Alice"

๐Ÿ“Œ tests/routes/test_user_routes.py

from fastapi.testclient import TestClient
from app.main import app

client = TestClient(app)

def test_get_users():
    response = client.get("/users")
    assert response.status_code == 200
    assert response.json()[0]["name"] == "Alice"

๐Ÿงช Mocking and Patching (without DB)

๐Ÿ“Œ Patch the Controller Function

from unittest.mock import patch
from fastapi.testclient import TestClient
from app.main import app

client = TestClient(app)

@patch("app.controllers.user_controller.list_users")
def test_get_users_mocked(mock_list_users):
    mock_list_users.return_value = [{"id": 1, "name": "Mock", "email": "mock@test.com"}]
    response = client.get("/users")
    assert response.status_code == 200
    assert response.json()[0]["name"] == "Mock"

๐Ÿงช Monkeypatching with pytest

๐Ÿ“Œ tests/controllers/test_user_controller.py

def fake_get_users():
    return [{"id": 99, "name": "Fake", "email": "fake@test.com"}]

def test_monkeypatch_user(monkeypatch):
    from app.models import user
    monkeypatch.setattr(user.User, "get_users", fake_get_users)
    from app.controllers.user_controller import list_users
    result = list_users()
    assert result[0]["name"] == "Fake"

๐Ÿ”Œ Fixtures in pytest (shared test data)

๐Ÿ“Œ tests/conftest.py

import pytest

@pytest.fixture
def test_user():
    return {"id": 1, "name": "Test", "email": "test@test.com"}

๐Ÿ“Œ Use the Fixture

def test_with_fixture(test_user):
    assert test_user["email"] == "test@test.com"

๐Ÿ” Integration Test (without Real DB)

๐Ÿ“Œ tests/integration/test_app.py

from fastapi.testclient import TestClient
from unittest.mock import patch
from app.main import app

client = TestClient(app)

def fake_users():
    return [{"id": 101, "name": "IntTest", "email": "int@test.com"}]

@patch("app.controllers.user_controller.list_users", side_effect=fake_users)
def test_full_integration(mock_list_users):
    response = client.get("/users")
    assert response.status_code == 200
    assert response.json()[0]["name"] == "IntTest"

๐Ÿ“Š Measure Code Coverage

pytest --cov=app tests/

✅ Summary Checklist

  • Follow MVC: models, schemas, controllers, routes

  • Use pytest for unit tests

  • Use mock, patch, monkeypatch to avoid real DB calls

  • Create integration tests using TestClient

  • Measure coverage with pytest-cov


Saturday

Reproducibility of Python

Ensuring the reproducibility of Python statistical analysis is crucial in research and scientific computing. Here are some ways to achieve reproducibility:

1. Version Control

Use version control systems like Git to track changes in your code and data.

2. Documentation

Document your code, methods, and results thoroughly.

3. Virtual Environments

Use virtual environments like conda or virtualenv to manage dependencies and ensure consistent package versions.

4. Seed Values

Set seed values for random number generators to ensure reproducibility of simulations and modeling results.

5. Data Management

Use data management tools like Pandas and NumPy to ensure data consistency and integrity.

6. Testing

Write unit tests and integration tests to ensure code correctness and reproducibility.

7. Containerization

Use containerization tools like Docker to package your code, data, and dependencies into a reproducible environment.

8. Reproducibility Tools

Utilize tools like Jupyter Notebook, Jupyter Lab, and Reproducible Research Tools to facilitate reproducibility.


Details these steps:


1. Use a Fixed Random Seed:

    ```python

    import numpy as np

    import random


    np.random.seed(42)

    random.seed(42)

    ```

2. Document the Environment:

    - List all packages and their versions.

    ```python

    import sys

    print(sys.version)

    

    !pip freeze > requirements.txt

    ```

3. Organize Code in Scripts or Notebooks:

    - Keep the analysis in well-documented scripts or Jupyter Notebooks.

4. Version Control:

    - Use version control systems like Git to track changes.

    ```bash

    git init

    git add .

    git commit -m "Initial commit"

    ```

5. Data Management:

    - Ensure data used in analysis is stored and accessed consistently.

    - Use data versioning tools like DVC (Data Version Control).

6. Environment Management:

    - Use virtual environments or containerization (e.g., `virtualenv`, `conda`, Docker).

    ```bash

    python -m venv env

    source env/bin/activate

    ```

7. Automated Tests:

    - Write tests to check the integrity of your analysis.

    ```python

    def test_mean():

        assert np.mean([1, 2, 3]) == 2

    ```

8. Detailed Documentation:

    - Provide clear and detailed documentation of your workflow.


By following these steps, you can ensure that your Python statistical analysis is reproducible.

Monday

Pytest with Django

 


Steps and code to set up Django Rest Framework (DRF) test cases with database mocking.


 1. Set up Django and DRF


Install Django and DRF:

```sh

pip install django djangorestframework

```


Create a Django project and app:

```sh

django-admin startproject projectname

cd projectname

python manage.py startapp appname

```


2. Define Models, Serializers, and Views


models.py (appname/models.py):

```python

from django.db import models


class Item(models.Model):

    name = models.CharField(max_length=100)

    description = models.TextField()

```


serializers.py (appname/serializers.py):

```python

from rest_framework import serializers

from .models import Item


class ItemSerializer(serializers.ModelSerializer):

    class Meta:

        model = Item

        fields = '__all__'

```


views.py (appname/views.py):

```python

from rest_framework import viewsets

from .models import Item

from .serializers import ItemSerializer


class ItemViewSet(viewsets.ModelViewSet):

    queryset = Item.objects.all()

    serializer_class = ItemSerializer

```


urls.py (appname/urls.py):

```python

from django.urls import path, include

from rest_framework.routers import DefaultRouter

from .views import ItemViewSet


router = DefaultRouter()

router.register(r'items', ItemViewSet)


urlpatterns = [

    path('', include(router.urls)),

]

```


projectname/urls.py:

```python

from django.contrib import admin

from django.urls import path, include


urlpatterns = [

    path('admin/', admin.site.urls),

    path('api/', include('appname.urls')),

]

```


3. Migrate Database and Create Superuser


```sh

python manage.py makemigrations appname

python manage.py migrate

python manage.py createsuperuser

python manage.py runserver

```


4. Write Test Cases


tests.py (appname/tests.py):

```python

from django.urls import reverse

from rest_framework import status

from rest_framework.test import APITestCase

from .models import Item

from .serializers import ItemSerializer


class ItemTests(APITestCase):

    

    def setUp(self):

        self.item1 = Item.objects.create(name='Item 1', description='Description 1')

        self.item2 = Item.objects.create(name='Item 2', description='Description 2')


    def test_get_items(self):

        url = reverse('item-list')

        response = self.client.get(url, format='json')

        items = Item.objects.all()

        serializer = ItemSerializer(items, many=True)

        self.assertEqual(response.status_code, status.HTTP_200_OK)

        self.assertEqual(response.data, serializer.data)


    def test_create_item(self):

        url = reverse('item-list')

        data = {'name': 'Item 3', 'description': 'Description 3'}

        response = self.client.post(url, data, format='json')

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

        self.assertEqual(Item.objects.count(), 3)

        self.assertEqual(Item.objects.get(id=3).name, 'Item 3')


    def test_update_item(self):

        url = reverse('item-detail', kwargs={'pk': self.item1.id})

        data = {'name': 'Updated Item 1', 'description': 'Updated Description 1'}

        response = self.client.put(url, data, format='json')

        self.assertEqual(response.status_code, status.HTTP_200_OK)

        self.item1.refresh_from_db()

        self.assertEqual(self.item1.name, 'Updated Item 1')


    def test_delete_item(self):

        url = reverse('item-detail', kwargs={'pk': self.item2.id})

        response = self.client.delete(url, format='json')

        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

        self.assertEqual(Item.objects.count(), 1)

```


5. Run Tests


```sh

python manage.py test

```


This setup provides a basic Django project with DRF and test cases for CRUD operations using the database. The test cases mock the database operations, ensuring isolation and consistency during testing.

Now diving into some more feature tests with Mock, patch etc.

Here are steps and code to write Django Rest Framework (DRF) test cases using mocking and faking features for scenarios like credit card processing.


1. Set up Django and DRF


Install Django and DRF:

```sh

pip install django djangorestframework

```


Create a Django project and app:

```sh

django-admin startproject projectname

cd projectname

python manage.py startapp appname

```


2. Define Models, Serializers, and Views


models.py (appname/models.py):

```python

from django.db import models


class Payment(models.Model):

    card_number = models.CharField(max_length=16)

    card_holder = models.CharField(max_length=100)

    expiration_date = models.CharField(max_length=5)

    amount = models.DecimalField(max_digits=10, decimal_places=2)

    status = models.CharField(max_length=10)

```


serializers.py (appname/serializers.py):

```python

from rest_framework import serializers

from .models import Payment


class PaymentSerializer(serializers.ModelSerializer):

    class Meta:

        model = Payment

        fields = '__all__'

```


views.py (appname/views.py):

```python

from rest_framework import viewsets

from .models import Payment

from .serializers import PaymentSerializer


class PaymentViewSet(viewsets.ModelViewSet):

    queryset = Payment.objects.all()

    serializer_class = PaymentSerializer

```


urls.py (appname/urls.py):

```python

from django.urls import path, include

from rest_framework.routers import DefaultRouter

from .views import PaymentViewSet


router = DefaultRouter()

router.register(r'payments', PaymentViewSet)


urlpatterns = [

    path('', include(router.urls)),

]

```


projectname/urls.py:

```python

from django.contrib import admin

from django.urls import path, include


urlpatterns = [

    path('admin/', admin.site.urls),

    path('api/', include('appname.urls')),

]

```


3. Migrate Database and Create Superuser


```sh

python manage.py makemigrations appname

python manage.py migrate

python manage.py createsuperuser

python manage.py runserver

```


4. Write Test Cases with Mocking and Faking


tests.py (appname/tests.py):

```python

from django.urls import reverse

from rest_framework import status

from rest_framework.test import APITestCase

from unittest.mock import patch

from .models import Payment

from .serializers import PaymentSerializer


class PaymentTests(APITestCase):


    def setUp(self):

        self.payment_data = {

            'card_number': '4111111111111111',

            'card_holder': 'John Doe',

            'expiration_date': '12/25',

            'amount': '100.00',

            'status': 'Pending'

        }

        self.payment = Payment.objects.create(**self.payment_data)

    

    @patch('appname.views.PaymentViewSet.create')

    def test_create_payment_with_mock(self, mock_create):

        mock_create.return_value = self.payment


        url = reverse('payment-list')

        response = self.client.post(url, self.payment_data, format='json')

        

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

        self.assertEqual(response.data['card_number'], self.payment_data['card_number'])


    @patch('appname.views.PaymentViewSet.perform_create')

    def test_create_payment_fake_response(self, mock_perform_create):

        def fake_perform_create(serializer):

            serializer.save(status='Success')


        mock_perform_create.side_effect = fake_perform_create


        url = reverse('payment-list')

        response = self.client.post(url, self.payment_data, format='json')

        

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

        self.assertEqual(response.data['status'], 'Success')


    def test_get_payments(self):

        url = reverse('payment-list')

        response = self.client.get(url, format='json')

        payments = Payment.objects.all()

        serializer = PaymentSerializer(payments, many=True)

        self.assertEqual(response.status_code, status.HTTP_200_OK)

        self.assertEqual(response.data, serializer.data)


    @patch('appname.views.PaymentViewSet.retrieve')

    def test_get_payment_with_mock(self, mock_retrieve):

        mock_retrieve.return_value = self.payment


        url = reverse('payment-detail', kwargs={'pk': self.payment.id})

        response = self.client.get(url, format='json')


        self.assertEqual(response.status_code, status.HTTP_200_OK)

        self.assertEqual(response.data['card_number'], self.payment_data['card_number'])


    @patch('appname.views.PaymentViewSet.update')

    def test_update_payment_with_mock(self, mock_update):

        mock_update.return_value = self.payment

        updated_data = self.payment_data.copy()

        updated_data['status'] = 'Completed'


        url = reverse('payment-detail', kwargs={'pk': self.payment.id})

        response = self.client.put(url, updated_data, format='json')


        self.assertEqual(response.status_code, status.HTTP_200_OK)

        self.assertEqual(response.data['status'], 'Completed')


    @patch('appname.views.PaymentViewSet.destroy')

    def test_delete_payment_with_mock(self, mock_destroy):

        mock_destroy.return_value = None


        url = reverse('payment-detail', kwargs={'pk': self.payment.id})

        response = self.client.delete(url, format='json')


        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

        self.assertEqual(Payment.objects.count(), 0)

```


5. Run Tests


```sh

python manage.py test

```


This setup uses `unittest.mock.patch` to mock the behavior of various viewset methods in DRF, allowing you to simulate different responses without hitting the actual database or external services.

House Based Manufacturing Micro Clustering

                                 image generated by meta ai House-based manufacturing micro-clustering in China refers to the hyper-local, v...