← Назад к вопросам

Какие виды тестов писал?

1.0 Junior🔥 231 комментариев
#Тестирование

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Виды тестов в моей практике Python-разработчика

За 10+ лет я писал все основные виды тестов. Каждый тип имеет свою цель и место в стратегии тестирования.

Unit-тесты

Тесты для отдельных функций и методов без внешних зависимостей:

# tests/test_validators.py
import pytest
from app.validators import EmailValidator, InvalidEmailError

class TestEmailValidator:
    @pytest.fixture
    def validator(self):
        return EmailValidator()
    
    def test_valid_email(self, validator):
        assert validator.validate('user@example.com') is True
    
    def test_invalid_format(self, validator):
        with pytest.raises(InvalidEmailError):
            validator.validate('invalid-email')
    
    def test_empty_string(self, validator):
        with pytest.raises(InvalidEmailError):
            validator.validate('')

Особенности: быстрые (< 100ms), изолированные, используют моки для внешних зависимостей.

Integration-тесты

Тесты взаимодействия нескольких компонентов, часто с БД:

# tests/integration/test_user_repository.py
import pytest
from app.models import User
from app.repositories import UserRepository
from sqlalchemy.orm import Session

@pytest.mark.integration
class TestUserRepository:
    @pytest.fixture
    def session(self, test_db):
        """Сессия для тестовой БД"""
        return test_db.session
    
    @pytest.fixture
    def repository(self, session):
        return UserRepository(session)
    
    def test_create_and_retrieve_user(self, repository):
        # Arrange
        user_data = {'name': 'John', 'email': 'john@example.com'}
        
        # Act
        user = repository.create(user_data)
        retrieved = repository.get_by_id(user.id)
        
        # Assert
        assert retrieved.name == 'John'
        assert retrieved.email == 'john@example.com'
    
    def test_update_user(self, repository):
        user = repository.create({'name': 'John', 'email': 'john@example.com'})
        repository.update(user.id, {'name': 'Jane'})
        
        updated = repository.get_by_id(user.id)
        assert updated.name == 'Jane'
    
    def test_delete_user(self, repository):
        user = repository.create({'name': 'John', 'email': 'john@example.com'})
        repository.delete(user.id)
        
        assert repository.get_by_id(user.id) is None

Особенности: медленнее (100ms-1s), требуют реальной БД или хорошего мока, проверяют интеграцию слоев.

E2E-тесты (End-to-End)

Полные тесты сценариев от пользователя через весь стек:

# tests/e2e/test_user_signup.py
import pytest
from fastapi.testclient import TestClient
from app.main import app

@pytest.mark.e2e
class TestUserSignup:
    @pytest.fixture
    def client(self):
        return TestClient(app)
    
    def test_user_signup_flow(self, client, test_db):
        # 1. Пользователь регистрируется
        response = client.post('/api/v1/auth/signup', json={
            'email': 'newuser@example.com',
            'password': 'SecurePass123'
        })
        assert response.status_code == 201
        user_id = response.json()['id']
        
        # 2. Пользователь логируется
        response = client.post('/api/v1/auth/login', json={
            'email': 'newuser@example.com',
            'password': 'SecurePass123'
        })
        assert response.status_code == 200
        token = response.json()['token']
        
        # 3. Пользователь обновляет профиль
        response = client.put(
            f'/api/v1/users/{user_id}',
            json={'name': 'John Doe'},
            headers={'Authorization': f'Bearer {token}'}
        )
        assert response.status_code == 200
        
        # 4. Проверяем сохранение
        response = client.get(
            f'/api/v1/users/{user_id}',
            headers={'Authorization': f'Bearer {token}'}
        )
        assert response.json()['name'] == 'John Doe'

Особенности: самые медленные (1-10s), проверяют всю систему, требуют развернутого окружения.

API-тесты (HTTP-тесты)

Тесты для REST API эндпоинтов:

# tests/api/test_users_endpoints.py
import pytest
from fastapi.testclient import TestClient

@pytest.mark.api
class TestUserAPI:
    @pytest.fixture
    def client(self):
        return TestClient(app)
    
    @pytest.fixture
    def auth_headers(self, client):
        response = client.post('/api/v1/auth/login', json={
            'email': 'admin@example.com',
            'password': 'password'
        })
        token = response.json()['token']
        return {'Authorization': f'Bearer {token}'}
    
    def test_list_users(self, client, auth_headers):
        response = client.get('/api/v1/users', headers=auth_headers)
        assert response.status_code == 200
        assert isinstance(response.json(), list)
    
    def test_get_nonexistent_user(self, client, auth_headers):
        response = client.get('/api/v1/users/999', headers=auth_headers)
        assert response.status_code == 404
    
    def test_unauthorized_access(self, client):
        response = client.get('/api/v1/users')
        assert response.status_code == 401

Load-тесты (тесты нагрузки)

Проверка производительности под нагрузкой с использованием locust или pytest-benchmark:

# tests/load/test_performance.py
import pytest
from locust import HttpUser, task, between

class LoadTestUser(HttpUser):
    wait_time = between(1, 3)
    
    @task
    def list_users(self):
        self.client.get('/api/v1/users')
    
    @task(3)
    def get_user(self):
        self.client.get('/api/v1/users/1')
    
    @task
    def create_user(self):
        self.client.post('/api/v1/users', json={
            'email': f'user{random.randint(1,10000)}@example.com',
            'name': 'Test User'
        })

# Или с pytest-benchmark
def test_user_creation_performance(benchmark):
    def create_user():
        return User.objects.create(name='Test', email='test@example.com')
    
    result = benchmark(create_user)
    assert result.id is not None

Mock-тесты

Тесты с подменой внешних зависимостей:

# tests/test_email_service.py
from unittest.mock import patch, MagicMock
import pytest

@patch('app.services.email.SMTPClient')
def test_send_email(mock_smtp):
    # Arrange
    mock_smtp.return_value.send = MagicMock(return_value=True)
    from app.services import EmailService
    service = EmailService()
    
    # Act
    result = service.send_email('user@example.com', 'Hello')
    
    # Assert
    assert result is True
    mock_smtp.return_value.send.assert_called_once()

Parametrized-тесты

Тесты с несколькими наборами данных:

import pytest

@pytest.mark.parametrize('email,is_valid', [
    ('valid@example.com', True),
    ('invalid.email', False),
    ('user@domain', False),
    ('test@test.co.uk', True),
])
def test_email_validation(email, is_valid):
    validator = EmailValidator()
    assert validator.validate(email) == is_valid

Test Coverage

Наше стандартное покрытие — минимум 90%:

# Проверить покрытие
pytest --cov=app --cov-report=html

# Результаты в htmlcov/index.html

Pyramid тестирования (правильное соотношение)

         /\
        /  \ E2E (5-10%)
       /    \
      /______\
     /        \ Integration (20-30%)
    /          \
   /____________\
  /              \ Unit tests (60-70%)
 /                \
/__________________\

Вывод: я использую полный спектр тестирования — от быстрых unit-тестов до полных E2E сценариев. Правильное сочетание типов тестов обеспечивает надежность системы при минимизации времени выполнения.

Какие виды тестов писал? | PrepBro