Почему «просто юнит-тестов» уже недостаточно?
Вы написали идеальный код на C++. Компиляция прошла, юнит-тесты зелёные, code review одобрили. Выкатываете релиз — и через час пользователи жалуются: «кнопка не нажимается», «база данных не отвечает», «GUI зависает». Знакомо?
Проблема в том, что юнит-тесты проверяют функцию, но не проверяют сценарий. Они изолированы. А в реальном мире ваш C++ код взаимодействует с сетью, файловой системой, GPU, внешними API и другими потоками. Именно здесь в игру вступает E2E тестирование C++ приложений (End-to-End или сквозное тестирование).
В этой статье мы разберём, как превратить хаос интеграционных ошибок в стройную систему автоматических проверок. Вы узнаете, какие инструменты реально работают, как писать тесты, которые не развалятся при первом запуске, и как встроить E2E в CI/CD пайплайн. Поехали!
Что такое E2E тестирование в контексте C++?
E2E тестирование — это проверка полного пользовательского сценария «от начала до конца». В отличие от модульных тестов, которые проверяют отдельную функцию, E2E тест запускает ваше приложение (или его критически важную часть) в среде, максимально приближенной к production.
Для C++ это может означать:
- Desktop приложение: запуск окна, клик по кнопке «Отправить», проверка, что данные ушли в БД.
- Серверное приложение: отправка HTTP-запроса к вашему REST API, проверка ответа и состояния базы.
- Embedded/ПО: подача сигнала на вход, проверка выходного сигнала через определённое время.
Ключевое отличие — реальная среда. Никаких mock-объектов для базы данных или сети. Только живое железо (или Docker).
Инструментарий: что реально работает для C++ в 2024?
Давайте сразу к делу. Вот стэк, который мы используем в бою:
- Catch2 или Google Test — для написания структуры тестов (Arrange-Act-Assert).
- Docker — для поднятия чистого окружения (БД, очереди, зависимости).
- Pytest + subprocess — для оркестрации (да, Python, но он идеален для склеивания C++ бинарников).
- Selenium/Appium — если у вас GUI на Qt или WebView.
- CMake + CTest — для запуска тестов и сбора результатов.
Не пытайтесь писать E2E тесты только на C++. Это больно. Используйте Python как клей — он быстрее для интеграционных сценариев.
Практика: пишем E2E тест для REST API на C++
Допустим, у нас есть C++ сервер на cpp-httplib (или crow, или drogon). Он принимает POST запросы по адресу /api/user и сохраняет пользователя в PostgreSQL.
Напишем E2E тест, который:
- Поднимает Docker контейнер с PostgreSQL.
- Компилирует и запускает наш сервер.
- Отправляет HTTP запрос через curl (или C++ клиент).
- Проверяет, что пользователь появился в БД.
Шаг 1: Docker-compose для тестового окружения
version: '3.8'
services:
db:
image: postgres:15
environment:
POSTGRES_DB: testdb
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
ports:
- "5432:5432"
app:
build: .
depends_on:
- db
ports:
- "8080:8080"
environment:
DB_HOST: db
Шаг 2: Тест на C++ с использованием Catch2
// test_e2e_api.cpp
#include <catch2/catch_test_macros.hpp>
#include <httplib.h>
#include <nlohmann/json.hpp>
#include <cstdlib>
TEST_CASE("E2E: Create user via API and verify in DB") {
// Arrange
httplib::Client cli("localhost", 8080);
nlohmann::json user = {
{"name", "John Doe"},
{"email", "john@example.com"}
};
// Act
auto res = cli.Post("/api/user", user.dump(), "application/json");
// Assert
REQUIRE(res->status == 201);
REQUIRE(res->body == "User created");
// Проверка через прямой запрос к БД (через системную команду)
// В реальном проекте лучше использовать libpqxx
std::string cmd = R"(psql -h localhost -U testuser -d testdb -c "SELECT * FROM users WHERE email='john@example.com';")";
int result = std::system(cmd.c_str());
REQUIRE(result == 0);
}
Этот тест запускается как обычный Catch2 бинарник. Но он требует, чтобы Docker был запущен и сервер работал. Как это автоматизировать?
Шаг 3: Оркестрация через Python (pytest + subprocess)
Создаём файл conftest.py:
import subprocess
import time
import pytest
@pytest.fixture(scope="session")
def docker_env():
# Поднимаем окружение
subprocess.run(["docker-compose", "up", "-d", "--build"], check=True)
time.sleep(5) # Ждём инициализации БД
yield
# Чистим после всех тестов
subprocess.run(["docker-compose", "down", "-v"], check=True)
def test_e2e_cpp_app(docker_env):
# Запускаем наш C++ тестовый бинарник
result = subprocess.run(
["./build/test_e2e_api"],
capture_output=True,
text=True
)
assert result.returncode == 0, f"E2E test failed:\n{result.stdout}\n{result.stderr}"
Теперь одной командой pytest мы поднимаем окружение, запускаем C++ тесты и чистим за собой. Это и есть настоящая автоматизация E2E тестирования C++ приложений.
E2E для Desktop приложений (Qt, wxWidgets)
Если ваше C++ приложение имеет GUI, подход меняется. Вам нужен инструмент, который умеет кликать по кнопкам и читать текст на экране.
Вариант 1: Squish (коммерческий, но мощный)
Squish от froglogic умеет записывать скрипты для Qt, QML и Web. Вы пишете тест на Python или JavaScript, а Squish эмулирует действия пользователя.
Вариант 2: OpenCV + pyautogui (бесплатно, но хрупко)
Для простых случаев можно находить кнопки по скриншотам:
import pyautogui
import subprocess
import time
# Запускаем приложение
proc =