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

В чем разница между функциональными и интеграционными тестами?

2.2 Middle🔥 131 комментариев
#Тестирование

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

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

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

Функциональные vs Интеграционные тесты

Это два разных уровня тестирования, которые проверяют разные аспекты приложения. Часто путают, но они решают совершенно разные задачи.

Функциональные тесты (Functional Tests)

Функциональные тесты проверяют, что система работает согласно требованиям с точки зрения пользователя. Они проверяют поведение приложения как целого, проходя через весь стек.

Характеристики:

  • Black-box тестирование — тестируем только входы и выходы
  • Фокус на пользовательском сценарии — "Может ли пользователь зарегистрироваться?"
  • Проверяет требования — соответствие функциональным требованиям
  • Может быть медленным — может вовлекать БД, сеть, файловую систему
  • E2E природа — от точки входа до точки выхода
  • Примеры: веб-тесты через браузер, API тесты
import pytest
from app import create_app
from db import Database

@pytest.fixture
def client():
    app = create_app()
    app.config['TESTING'] = True
    with app.test_client() as client:
        yield client

def test_user_registration(client):
    """Функциональный тест: пользователь может зарегистрироваться"""
    # Пользователь отправляет форму
    response = client.post('/api/register', json={
        'email': 'user@example.com',
        'password': 'password123'
    })
    
    # Проверяем ответ
    assert response.status_code == 201
    assert response.json['id'] is not None
    
    # Проверяем, что пользователь действительно создан
    user = client.get(f"/api/users/{response.json['id']}")
    assert user.json['email'] == 'user@example.com'

def test_user_login_workflow(client):
    """Функциональный тест: весь цикл входа пользователя"""
    # Шаг 1: Регистрация
    register = client.post('/api/register', json={
        'email': 'user@example.com',
        'password': 'password123'
    })
    assert register.status_code == 201
    
    # Шаг 2: Логин
    login = client.post('/api/login', json={
        'email': 'user@example.com',
        'password': 'password123'
    })
    assert login.status_code == 200
    token = login.json['token']
    
    # Шаг 3: Используем токен
    headers = {'Authorization': f'Bearer {token}'}
    profile = client.get('/api/me', headers=headers)
    assert profile.status_code == 200
    assert profile.json['email'] == 'user@example.com'

Интеграционные тесты (Integration Tests)

Интеграционные тесты проверяют, что отдельные компоненты системы работают вместе правильно. Они тестируют взаимодействие между модулями, но меньше, чем функциональные тесты.

Характеристики:

  • White-box/grey-box тестирование — знаем внутреннюю структуру
  • Фокус на компонентах — тестируем их взаимодействие
  • Проверяет интеграцию — работают ли модули вместе
  • Быстрее функциональных — могут мокировать внешние системы
  • Часть цепочки — тестируем несколько компонентов, но не всё
  • Примеры: тесты базы данных и API, микросервисы общаются
import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from services.user_service import UserService
from services.auth_service import AuthService
from models import User, Base

@pytest.fixture
def db_session():
    """Интеграционная БД для тестов"""
    engine = create_engine('sqlite:///:memory:')
    Base.metadata.create_all(engine)
    Session = sessionmaker(bind=engine)
    session = Session()
    yield session
    session.close()

def test_user_service_saves_to_database(db_session):
    """Интеграционный тест: UserService сохраняет в БД"""
    user_service = UserService(db_session)
    
    # Создаём пользователя через service
    user = user_service.create_user(
        email='user@example.com',
        password='password123'
    )
    
    # Проверяем, что действительно в БД
    saved_user = db_session.query(User).filter_by(email='user@example.com').first()
    assert saved_user is not None
    assert saved_user.id == user.id

def test_auth_service_integrates_with_user_service(db_session):
    """Интеграционный тест: AuthService работает с UserService"""
    user_service = UserService(db_session)
    auth_service = AuthService(db_session, user_service)
    
    # AuthService создаёт пользователя через UserService
    token = auth_service.register(
        email='user@example.com',
        password='password123'
    )
    
    # Проверяем, что токен валиден и соответствует пользователю
    user = auth_service.get_user_from_token(token)
    assert user.email == 'user@example.com'

def test_services_with_mocked_external_api(db_session):
    """Интеграционный тест с мокированием внешних ресурсов"""
    from unittest.mock import Mock, patch
    
    user_service = UserService(db_session)
    
    # Мокируем внешний сервис (например, email отправка)
    with patch('services.user_service.EmailService') as mock_email:
        user = user_service.create_user_and_send_email(
            email='user@example.com',
            password='password123'
        )
        
        # Проверяем интеграцию: user создан И email отправлен
        assert user.id is not None
        mock_email.send.assert_called_once()

Сравнение в таблице

АспектФункциональныеИнтеграционные
ФокусПользовательский сценарийВзаимодействие компонентов
ОхватВесь стек (E2E)Часть системы
СкоростьМедленнееБыстрее
Знание внутреннегоBlack-boxGrey-box
ПримерыUI тесты, API workflowsUnit → Integration
Внешние системыРеальные или мокированныеОбычно мокированные
Уровень пирамидыВершинаСередина

Пирамида тестирования

        ┌─────────────────┐
        │   E2E тесты     │  Функциональные (медленные, редкие)
        │   (Selenium)    │
        ├─────────────────┤
        │ Интеграционные  │  Компоненты вместе (средние)
        │    (API, БД)    │
        ├─────────────────┤
        │   Unit тесты    │  Функции отдельно (быстрые, много)
        │   (Pytest)      │
        └─────────────────┘

Практический пример: одна сценария разными типами тестов

Unit тест (самый быстрый)

def test_password_hashing():
    """Unit: проверяем функцию хеширования пароля"""
    from security import hash_password
    
    hashed = hash_password('password123')
    assert hashed != 'password123'
    assert len(hashed) > 20

Интеграционный тест

def test_user_service_hashes_password(db_session):
    """Интеграционный: UserService правильно хеширует"""
    from services.user_service import UserService
    
    service = UserService(db_session)
    user = service.create_user('user@ex.com', 'password123')
    
    # Проверяем, что в БД пароль захеширован
    assert user.password_hash != 'password123'

Функциональный тест

def test_user_cannot_login_with_wrong_password(client):
    """Функциональный: пользователь не может войти с неправильным паролем"""
    # Регистрируемся
    client.post('/api/register', json={
        'email': 'user@ex.com',
        'password': 'password123'
    })
    
    # Пытаемся войти с неправильным паролем
    response = client.post('/api/login', json={
        'email': 'user@ex.com',
        'password': 'wrongpassword'
    })
    
    assert response.status_code == 401

Когда писать что

Функциональные тесты — для:

  • Критичных user journeys — регистрация, оплата, логин
  • Регрессионного тестирования — убедиться, что всё ещё работает
  • Приёмочного тестирования — подтвердить требования

Интеграционные тесты — для:

  • Взаимодействия сервисов — AuthService + UserService
  • Работы с реальной БД — но в изолированной среде
  • API endpoints — с настоящей БД

Практические рекомендации

# ПЛОХО: смешивание уровней
def test_everything(client, db_session):
    # Unit + интеграция + функциональное
    response = client.post('/api/register', ...)
    user = db_session.query(User).filter(...)
    assert hash_password(...) == user.password_hash

# ХОРОШО: разделение ответственности

# Unit тест
def test_hash_password():
    assert hash_password('pwd') != 'pwd'

# Интеграционный
def test_user_service_saves_hashed(db_session):
    service = UserService(db_session)
    user = service.create_user('u@ex.com', 'pwd')
    assert db_session.query(User).filter_by(id=user.id).first().password_hash == user.password_hash

# Функциональный
def test_register_endpoint(client):
    response = client.post('/api/register', json={'email': 'u@ex.com', 'password': 'pwd'})
    assert response.status_code == 201

Заключение

Функциональные тесты проверяют, что система работает с точки зрения пользователя — весь цикл от входа до выхода. Интеграционные тесты проверяют, что компоненты взаимодействуют правильно, но обычно мокируют внешние системы. В правильной пирамиде тестов много unit-тестов (быстрых), средне интеграционных, и мало функциональных (медленных). Оба типа критичны, но покрывают разные требования.