С какими системами управления тестированием (TMS) вы имели опыт работы?

Ответ

Работал с несколькими TMS, интегрируя их в CI/CD для разных проектов.

Система Основное применение и преимущества
TestRail Основная TMS для ручного тестирования и хранения сценариев. Отличная интеграция с JIRA, удобное API для автоматической отправки результатов из CI.
Allure TestOps Мощный инструмент для проектов с упором на автоматизацию. Позволяет связывать автотесты (из кода) с ручными тест-кейсами, строить insightful дашборды и аналитику по покрытию.
Zephyr Scale Гибкое решение внутри Jira. Хорошо подходит для BDD-подхода (поддержка Gherkin), удобное управление тест-планами и циклами.
Xray Стандарт для многих команд, работающих строго в экосистеме Atlassian. Удобен для привязки тестов к требованиям (Jira issues) и генерации traceability reports.

Практический пример интеграции: Автоматическая отправка результатов pytest в TestRail после прогона в Jenkins.

# utils/testrail_integration.py
import requests
from typing import Literal

def publish_result_to_testrail(
    run_id: int,
    case_id: int,
    status: Literal["passed", "failed", "blocked"],
    comment: str = "",
    duration: int = 0
) -> dict:
    """
    Отправляет результат выполнения автотеста в TestRail.
    """
    # Маппинг статусов pytest -> TestRail
    status_map = {
        "passed": 1,  # TestRail: Passed
        "failed": 5,  # TestRail: Failed
        "blocked": 2, # TestRail: Blocked
    }

    url = f"{TESTRAIL_URL}/index.php?/api/v2/add_result_for_case/{run_id}/{case_id}"
    headers = {"Content-Type": "application/json"}

    # Использование API Key (рекомендуется) или базовой аутентификации
    auth = (API_USER, API_KEY)

    payload = {
        "status_id": status_map[status],
        "comment": f"Автоматический прогон.n{comment}",
        "elapsed": f"{duration}s"
    }

    response = requests.post(url, json=payload, headers=headers, auth=auth)
    response.raise_for_status()  # Выбросит исключение при ошибке HTTP
    return response.json()

# В фикстуре pytest после выполнения теста
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    """Хук для получения результата теста и отправки в TestRail."""
    outcome = yield
    report = outcome.get_result()

    if report.when == "call" and hasattr(item, "testrail_id"):
        status = "passed" if report.passed else "failed"
        publish_result_to_testrail(
            run_id=ITEM.run_id,
            case_id=item.testrail_id,
            status=status,
            comment=report.longreprtext if report.failed else "",
            duration=int(report.duration)
        )

Выбор TMS зависит от процессов команды: TestRail — для классического разделения ручного/автотестирования, Allure TestOps — для data-driven решений с глубокой аналитикой, Zephyr/Xray — для tight integration с Jira.

Ответ 18+ 🔞

Да ты посмотри, какая хуйня развелась — систем для управления тестированием, как собак нерезаных! Сидишь, блядь, выбираешь, а нихуя не понятно, куда впендюрить свой CI/CD, чтобы всё летало, а не ползало, как мартышлюшка по стеклу.

Вот, нарыл я за свою практику несколько штук, потрогал их за всё, что можно. Смотри, табличка, чтоб не ебать мозги:

Система На кой хрен нужна и где не обосрёшься
TestRail Это, блядь, классика для ручных тестировщиков, чтоб сценарии хранить. К JIRA прилипает, как жопа к дивану после трёх литров пива. API у неё — просто песня, можно из CI результаты автоматом пихать.
Allure TestOps О, это для тех, у кого автотестов — овердохуища! Тут можно связать код с ручными кейсами, дашборды строить такие, что директор аж прослезится от покрытия. Инсайты, блядь, а не инструмент.
Zephyr Scale Штука, которая в Jira живёт, как паразит. Если ты BDD-шник и любишь этот Gherkin-бред — тебе сюда. Планы и циклы тестов крутишь, как хочешь.
Xray Ну, это стандартный пидарас в мире Atlassian. Если вся команда дышит Jira, а отчёты о traceability тебе снится по ночам — бери, не прогадаешь.

А теперь, сука, практика! Как, блядь, заставить Jenkins после прогона пихнуть результаты в TestRail, чтобы не делать это руками, как последний лузер.

# utils/testrail_integration.py
import requests
from typing import Literal

def publish_result_to_testrail(
    run_id: int,
    case_id: int,
    status: Literal["passed", "failed", "blocked"],
    comment: str = "",
    duration: int = 0
) -> dict:
    """
    Отправляет результат выполнения автотеста в TestRail.
    """
    # Маппинг статусов pytest -> TestRail
    status_map = {
        "passed": 1,  # TestRail: Passed
        "failed": 5,  # TestRail: Failed
        "blocked": 2, # TestRail: Blocked
    }

    url = f"{TESTRAIL_URL}/index.php?/api/v2/add_result_for_case/{run_id}/{case_id}"
    headers = {"Content-Type": "application/json"}

    # Использование API Key (рекомендуется) или базовой аутентификации
    auth = (API_USER, API_KEY)

    payload = {
        "status_id": status_map[status],
        "comment": f"Автоматический прогон.n{comment}",
        "elapsed": f"{duration}s"
    }

    response = requests.post(url, json=payload, headers=headers, auth=auth)
    response.raise_for_status()  # Выбросит исключение при ошибке HTTP
    return response.json()

# В фикстуре pytest после выполнения теста
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    """Хук для получения результата теста и отправки в TestRail."""
    outcome = yield
    report = outcome.get_result()

    if report.when == "call" and hasattr(item, "testrail_id"):
        status = "passed" if report.passed else "failed"
        publish_result_to_testrail(
            run_id=ITEM.run_id,
            case_id=item.testrail_id,
            status=status,
            comment=report.longreprtext if report.failed else "",
            duration=int(report.duration)
        )

Короче, выбор-то, блядь, от чего зависит? Да от того, как твоя команда устроена. TestRail — если у тебя ручники и автоматчики живут отдельно. Allure TestOps — если ты data-driven-хуйнёй страдаешь и аналитику любишь. Zephyr/Xray — если ты, блядь, сросся с Jira и не можешь от неё отлипнуть. Вот и вся философия, ёпта!