Документация Руководство по SDK

Руководство по SDK

Mockarty предоставляет официальные SDK-библиотеки для Go, Python, Java и Kotlin. Все SDK обеспечивают типобезопасный доступ к API Mockarty для управления моками, тестирования, фаззинга, контрактного тестирования, нагрузочного тестирования и многого другого.

Все SDK следуют единым принципам проектирования: fluent-билдеры для создания моков, согласованные имена API между языками, настройка через переменные окружения и встроенная интеграция с тестовыми фреймворками.

URL-адреса в примерах: Во всех примерах используется localhost:5770 как адрес Mockarty по умолчанию. Если ваш экземпляр работает на удалённом сервере, замените localhost:5770 на реальный адрес (например, https://mockarty.company.com или http://192.168.1.50:5770). Подробнее — в разделе Полезные функции и советы.

Прогон системы под тестом: помимо настройки mock’ов SDK предоставляют тестовые клиенты для gRPC, Kafka, RabbitMQ, SOAP, GraphQL, SSE, WebSocket с автоматическим step-capture в external-прогоны TCM. См. Протокольные клиенты SDK — кросс-языковой справочник.

Поддерживаемые языки

Язык Пакет GitHub Статус
Go github.com/mockarty/mockarty-go github.com/mockarty/mockarty-go Стабильный
Python mockarty (PyPI) github.com/mockarty/py-sdk Стабильный
Java ru.mockarty:mockarty-java (Maven Central) github.com/mockarty/java-sdk Стабильный
Kotlin ru.mockarty:mockarty-kotlin (Maven Central) github.com/mockarty/java-sdk Стабильный

Установка

Go

go get github.com/mockarty/mockarty-go

Требования: Go 1.21+, без внешних зависимостей (только стандартная библиотека).

Python

# pip
pip install mockarty

# Poetry
poetry add mockarty

# С поддержкой async HTTP/2
pip install mockarty[async]

Требования: Python 3.9+.

Java (Maven)

<dependency>
    <groupId>ru.mockarty</groupId>
    <artifactId>mockarty-java</artifactId>
    <version>1.0.0</version>
</dependency>

<!-- Расширение JUnit 5 (тестовая область) -->
<dependency>
    <groupId>ru.mockarty</groupId>
    <artifactId>mockarty-junit5</artifactId>
    <version>1.0.0</version>
    <scope>test</scope>
</dependency>

Java (Gradle)

// Основной SDK
implementation("ru.mockarty:mockarty-java:1.0.0")

// Расширение JUnit 5
testImplementation("ru.mockarty:mockarty-junit5:1.0.0")

Kotlin

implementation("ru.mockarty:mockarty-kotlin:1.0.0")

Предоставляет Kotlin DSL расширения поверх Java SDK.

Настройка клиента

Go

import mockarty "github.com/mockarty/mockarty-go"

client := mockarty.NewClient("http://localhost:5770",
    mockarty.WithAPIKey("mk_your_api_key"),   // Аутентификация по API-ключу
    mockarty.WithNamespace("production"),      // Пространство имён (по умолчанию: "sandbox")
    mockarty.WithTimeout(10 * time.Second),   // Таймаут HTTP (по умолчанию: 30с)
    mockarty.WithRetry(3, time.Second),       // Повторные попытки с экспоненциальной задержкой
    mockarty.WithHTTPClient(customClient),    // Пользовательский http.Client
    mockarty.WithLogger(slog.Default()),      // Структурированный логгер
)

Python

from mockarty import MockartyClient, AsyncMockartyClient

# Синхронный клиент
client = MockartyClient(
    base_url="http://localhost:5770",
    api_key="mk_your_api_key",
    namespace="sandbox",
    timeout=30,
    max_retries=3,
)

# Асинхронный клиент (требует mockarty[async])
async_client = AsyncMockartyClient(
    base_url="http://localhost:5770",
    api_key="mk_your_api_key",
)

# Поддержка контекстного менеджера
with MockartyClient() as client:
    client.mocks.create(mock)
    # client.close() вызывается автоматически

# Асинхронный контекстный менеджер
async with AsyncMockartyClient() as client:
    await client.mocks.create(mock)

Java

import ru.mockarty.MockartyClient;

// Паттерн Builder, поддержка AutoCloseable
try (MockartyClient client = MockartyClient.builder()
        .baseUrl("http://localhost:5770")
        .apiKey("mk_your_api_key")
        .namespace("sandbox")
        .timeout(Duration.ofSeconds(30))
        .build()) {

    client.mocks().create(mock);
}

Kotlin

import ru.mockarty.MockartyClient

// Сокращённая фабрика
val client = MockartyClient.create("http://localhost:5770", "mk_your_api_key")

Переменные окружения

Все SDK поддерживают настройку через переменные окружения:

Переменная Описание По умолчанию
MOCKARTY_BASE_URL URL сервера Mockarty http://localhost:5770
MOCKARTY_API_KEY Ключ аутентификации API (нет)
MOCKARTY_NAMESPACE Пространство имён по умолчанию sandbox

Справочник API

API моков

Создание, чтение, обновление и удаление моков. Поддержка пакетных операций, мягкого удаления/восстановления, версионирования и доступа к логам.

Go

// Создание
resp, err := client.Mocks().Create(ctx, mock)

// Получение / Список
mock, err := client.Mocks().Get(ctx, "mock-id")
list, err := client.Mocks().List(ctx, &mockarty.ListMocksOptions{
    Namespace: "production",
    Tags:      []string{"users"},
    Limit:     20,
})

// Обновление / Удаление / Восстановление / Полное удаление
updated, err := client.Mocks().Update(ctx, "mock-id", mock)
err = client.Mocks().Delete(ctx, "mock-id")
err = client.Mocks().Restore(ctx, "mock-id")
err = client.Mocks().Purge(ctx, "mock-id")

// Пакетные операции
err = client.Mocks().BatchCreate(ctx, mocks)
err = client.Mocks().BatchDelete(ctx, ids)
err = client.Mocks().BatchRestore(ctx, ids)

// Логи и версии
logs, err := client.Mocks().Logs(ctx, "mock-id", &mockarty.LogsOptions{Limit: 50})
versions, err := client.Mocks().Versions(ctx, "chain-id")

Python

# Создание
result = client.mocks.create(mock)

# Получение / Список
mock = client.mocks.get("mock-id")
page = client.mocks.list(namespace="sandbox", limit=10)
for m in page.items:
    print(m.id)

# Удаление / Восстановление
client.mocks.delete("mock-id")
client.mocks.restore("mock-id")

Java

// Создание
client.mocks().create(mock);

// Получение / Список
Mock mock = client.mocks().get("mock-id");

// Удаление / Восстановление
client.mocks().delete("mock-id");
client.mocks().restore("mock-id");

API хранилищ

Доступ к глобальным и цепочечным хранилищам для сценариев с состоянием.

Go

// Глобальное хранилище
store, err := client.Stores().GlobalGet(ctx)
err = client.Stores().GlobalSet(ctx, "counter", 42)
err = client.Stores().GlobalDelete(ctx, "key1")
err = client.Stores().GlobalDeleteMany(ctx, "key1", "key2")

// Цепочечное хранилище
store, err := client.Stores().ChainGet(ctx, "chain-id")
err = client.Stores().ChainSet(ctx, "chain-id", "status", "completed")
err = client.Stores().ChainDelete(ctx, "chain-id", "key")
err = client.Stores().ChainDeleteMany(ctx, "chain-id", "key1", "key2")

Python

# Глобальное хранилище
client.stores.global_set("counter", 0)
data = client.stores.global_get()

# Цепочечное хранилище
client.stores.chain_set("order-flow", "orderId", "abc-123")
chain_data = client.stores.chain_get("order-flow")

Java

// Глобальное хранилище
client.stores().globalSet("counter", 0);
Map<String, Object> store = client.stores().globalGet();

// Цепочечное хранилище
client.stores().chainSet("registration-flow", "step", "1");

API пространств имён

Создание и получение списка пространств имён для изоляции моков.

Go

err := client.Namespaces().Create(ctx, "production")
namespaces, err := client.Namespaces().List(ctx)

Python

client.namespaces.create("production")
namespaces = client.namespaces.list()

Java

client.namespaces().create("production");

API коллекций и тестовых запусков

Запуск коллекций API-тестов и получение результатов.

Go

collections, err := client.Collections().List(ctx)
result, err := client.Collections().Execute(ctx, "collection-id")

Python

collections = client.collections.list()
result = client.collections.execute("collection-id")

Java

client.collections().list();
client.collections().execute("collection-id");

API нагрузочного тестирования

Запуск нагрузочных тестов, управление конфигурациями и расписаниями, сравнение результатов.

Go

task, err := client.Perf().Run(ctx, &mockarty.PerfConfig{...})
err = client.Perf().Stop(ctx, "task-id")
results, err := client.Perf().Results(ctx)

Python

task = client.perf.run(config)
client.perf.stop("task-id")
results = client.perf.results()

Java

client.perf().run(config);
client.perf().stop("task-id");
client.perf().results();

API фаззинга

Запуск сессий фаззинга безопасности, управление конфигурациями и триаж находок.

Go

task, err := client.Fuzzing().Start(ctx, &mockarty.FuzzConfig{...})
findings, err := client.Fuzzing().ListFindings(ctx)

Python

task = client.fuzzing.start(config)
findings = client.fuzzing.list_findings()

Java

client.fuzzing().start(config);
client.fuzzing().listFindings();

API контрактного тестирования

Валидация моков по API-спецификациям, верификация провайдеров, управление Pact-контрактами, проверки can-i-deploy и использование реестра API для управления контрактами между командами.

Go

// Валидация моков по OpenAPI/Swagger-спецификации
result, err := client.Contracts().ValidateMocks(ctx, &mockarty.ContractValidationRequest{
    SpecURL:   "https://petstore.swagger.io/v2/swagger.json",
    Namespace: "production",
})

// Верификация живого провайдера по контракту
verify, err := client.Contracts().VerifyProvider(ctx, &mockarty.ContractValidationRequest{
    SpecURL:   "https://api.example.com/openapi.json",
    TargetURL: "https://api.example.com",
})

// Поддержка Pact
pacts, err := client.Contracts().ListPacts(ctx)
published, err := client.Contracts().PublishPact(ctx, &mockarty.Pact{
    Consumer: "frontend",
    Provider: "user-service",
    Spec:     pactJSON,
})
deploy, err := client.Contracts().CanIDeploy(ctx, canIDeployReq)
mocks, err := client.Contracts().GenerateMocksFromPact(ctx, "pact-id")

// Обнаружение дрейфа между контрактом и живым сервисом
drift, err := client.Contracts().DetectDrift(ctx, driftReq)

// Реестр API: публикация, подписка, проверка ломающих изменений
entries, err := client.Contracts().ListRegistry(ctx, "")
entry, err := client.Contracts().PublishToRegistry(ctx, &mockarty.RegistryEntry{
    ServiceName: "user-service",
    SpecType:    "openapi",
    Version:     "2.0.0",
})
sub, err := client.Contracts().Subscribe(ctx, "entry-id", &mockarty.Subscription{
    ServiceName:      "frontend",
    NotifyOnBreaking: true,
})
impact, err := client.Contracts().CheckImpact(ctx, "entry-id", newSpecContent)

// Конфиги и результаты
configs, err := client.Contracts().ListConfigs(ctx)
results, err := client.Contracts().ListResults(ctx)

Python

result = client.contracts.validate_mocks({"specUrl": "...", "namespace": "production"})
pacts = client.contracts.list_pacts()
deploy = client.contracts.can_i_deploy(request)
entries = client.contracts.list_registry()

Java

client.contracts().validateMocks(request);
client.contracts().listPacts();
client.contracts().canIDeploy(request);
client.contracts().listRegistry("");

API записи трафика

Захват живого трафика и генерация моков из записанных запросов.

Go

// Запуск сессии записи
session, err := client.Recorder().StartRecording(ctx, &mockarty.RecorderSession{
    Name:      "capture-login-flow",
    TargetURL: "https://api.example.com",
})

// Управление сессиями
sessions, err := client.Recorder().ListSessions(ctx)
session, err = client.Recorder().GetSession(ctx, "session-id")
err = client.Recorder().StopRecording(ctx, "session-id")
err = client.Recorder().RestartRecording(ctx, "session-id")

// Работа с записанными запросами
entries, err := client.Recorder().GetEntries(ctx, "session-id")
err = client.Recorder().AnnotateEntry(ctx, "session-id", "entry-id", annotation)
err = client.Recorder().ReplayEntry(ctx, "session-id", "entry-id")

// Генерация моков из записанного трафика
mocks, err := client.Recorder().CreateMocksFromSession(ctx, "session-id", nil)

// Экспорт сессии как HAR
har, err := client.Recorder().ExportSession(ctx, "session-id")

// Управление CA-сертификатом (для записи HTTPS)
caStatus, err := client.Recorder().GetCAStatus(ctx)
err = client.Recorder().GenerateCA(ctx)
caCert, err := client.Recorder().DownloadCA(ctx)

Python

session = client.recorder.create(config)
sessions = client.recorder.list()
session = client.recorder.get("session-id")
client.recorder.stop("session-id")
entries = client.recorder.entries("session-id")
mocks = client.recorder.generate_mocks("session-id")

Java

RecorderSession s = client.recorder().start(config);
client.recorder().listSessions();
client.recorder().getSession("session-id");
client.recorder().stopRecording("session-id");
client.recorder().getEntries("session-id");
client.recorder().createMocks("session-id", null);

API здоровья

Проверка состояния сервера, liveness и readiness пробы.

Go

health, err := client.Health().Check(ctx)
err = client.Health().Live(ctx)
err = client.Health().Ready(ctx)

Python

health = client.health.check()
print(health.status)

Java

boolean healthy = client.health().ready();

API хаос-инженерии

BETA — Управляемые эксперименты по внедрению отказов в Kubernetes-кластерах. Подробнее в руководстве по хаос-инженерии.

Go

// Профили кластеров (подключение к Kubernetes)
profiles, err := client.Chaos().ListProfiles(ctx)
profile, err := client.Chaos().CreateProfile(ctx, &mockarty.ChaosProfile{
    Name:           "staging-cluster",
    KubeconfigData: base64Kubeconfig,
    Context:        "staging-ctx",
})
connResult, err := client.Chaos().TestProfile(ctx, profile.ID)

// Список пресетов (готовые шаблоны экспериментов)
presets, err := client.Chaos().ListPresets(ctx)

// Создание и запуск эксперимента
exp, err := client.Chaos().Create(ctx, &mockarty.ChaosExperiment{
    Name:        "pod-kill-test",
    Namespace:   "staging",
    DurationSec: 300,
    Faults: []mockarty.FaultConfig{{
        Type:           mockarty.FaultPodKillRandom,
        GracePeriodSec: 5,
    }},
    Target: mockarty.TargetConfig{
        Mode:     mockarty.TargetModeAll,
        Selector: map[string]string{"app": "frontend"},
    },
    Safety: mockarty.SafetyConfig{
        MinReplicasAlive: 1,
        AutoRollback:     true,
    },
})
err = client.Chaos().Run(ctx, exp.ID)

// Мониторинг эксперимента
metrics, err := client.Chaos().GetMetrics(ctx, exp.ID)
events, err := client.Chaos().GetEvents(ctx, exp.ID)
err = client.Chaos().Abort(ctx, exp.ID) // экстренная остановка

// Получение отчёта и скачивание
report, err := client.Chaos().GetReport(ctx, exp.ID)
htmlReport, err := client.Chaos().DownloadReport(ctx, exp.ID, "html")

// Операции с кластером (ad-hoc)
topology, err := client.Chaos().GetTopology(ctx, profile.ID, "default")
err = client.Chaos().KillPod(ctx, "staging", "frontend-abc123", 0)
podDetail, err := client.Chaos().GetPodDetail(ctx, "staging", "frontend-abc123")
logs, err := client.Chaos().GetPodLogs(ctx, "staging", "frontend-abc123", "app", 100)
result, err := client.Chaos().ScaleDeployment(ctx, "staging", "frontend", 3)

// Управление оператором
opStatus, err := client.Chaos().GetOperatorStatus(ctx, "chaos-system")
manifest, err := client.Chaos().GenerateOperatorManifest(ctx, "https://mockarty.example.com", "")

Python

# Список пресетов
presets = client.chaos.list_presets()

# Создание и запуск эксперимента
exp = client.chaos.create({
    "name": "pod-kill-test",
    "namespace": "staging",
    "durationSec": 300,
    "faults": [{"type": "pod_kill_random"}],
    "target": {"mode": "all", "selector": {"app": "frontend"}},
    "safety": {"minReplicasAlive": 1, "autoRollback": True},
})
client.chaos.run(exp["id"])

# Получение отчёта
report = client.chaos.get_report(exp["id"])

Java

// Создание и запуск эксперимента
ChaosExperiment exp = client.chaos().create(new ChaosExperiment()
    .setName("pod-kill-test")
    .setNamespace("staging")
    .setDurationSec(300));
client.chaos().run(exp.getId());

// Получение отчёта
Map<String, Object> report = client.chaos().getReport(exp.getId());

Билдер моков (Fluent API)

Все SDK предоставляют fluent-билдер для конструирования моков с полной типобезопасностью.

HTTP-мок с условиями

Go

mock := mockarty.NewMockBuilder().
    ID("create-order").
    HTTP(func(h *mockarty.HTTPBuilder) {
        h.Route("/api/orders").
          Method("POST").
          HeaderCondition("Authorization", mockarty.AssertNotEmpty, nil)
    }).
    Response(func(r *mockarty.ResponseBuilder) {
        r.Status(201).
          Header("Content-Type", "application/json").
          JSONBody(map[string]any{
              "orderId": "$.fake.UUID",
              "amount":  "$.req.amount",
              "status":  "created",
          })
    }).
    Build()

Python

from mockarty import MockBuilder, AssertAction

mock = (
    MockBuilder.http("/api/orders", "POST")
    .id("create-order")
    .namespace("production")
    .tags("orders", "v2")
    .priority(10)
    .condition("$.body.amount", AssertAction.NOT_EMPTY)
    .header_condition("Authorization", AssertAction.NOT_EMPTY)
    .respond(201, body={
        "orderId": "$.fake.UUID",
        "amount": "$.req.amount",
        "status": "created",
    })
    .callback("https://webhook.site/test", method="POST", body={"event": "order.created"})
    .ttl(7200)
    .build()
)

Java

import ru.mockarty.builder.MockBuilder;
import ru.mockarty.model.AssertAction;

Mock mock = MockBuilder.http("/api/orders", "POST")
    .id("create-order")
    .condition("$.body.amount", AssertAction.NOT_EMPTY, null)
    .headerCondition("Authorization", AssertAction.NOT_EMPTY, null)
    .queryCondition("format", AssertAction.EQUALS, "json")
    .respond(201, Map.of(
        "orderId", "$.fake.UUID",
        "amount", "$.req.amount",
        "status", "created"
    ))
    .callback("https://webhook.example.com/notify", "POST",
        Map.of("event", "order.created"))
    .ttl(3600)
    .build();

Kotlin

import ru.mockarty.dsl.*
import ru.mockarty.model.AssertAction

client.createMock {
    id = "create-order"
    namespace = "production"
    tags = listOf("orders", "v2")

    http {
        route = "/api/orders"
        method = "POST"
        headerCondition("Authorization", AssertAction.NOT_EMPTY)
    }

    respond {
        statusCode = 201
        body = mapOf(
            "orderId" to "$.fake.UUID",
            "amount" to "$.req.amount",
            "status" to "created"
        )
    }

    ttl = 3600
}

gRPC-мок

Go

mock := mockarty.NewMockBuilder().
    ID("grpc-user").
    GRPC(func(g *mockarty.GRPCBuilder) {
        g.Service("UserService").Method("GetUser")
    }).
    Response(func(r *mockarty.ResponseBuilder) {
        r.JSONBody(map[string]any{"name": "John", "email": "john@test.com"})
    }).
    Build()

Python

grpc_mock = (
    MockBuilder.grpc("user.UserService", "GetUser")
    .id("grpc-get-user")
    .condition("$.id", AssertAction.NOT_EMPTY)
    .respond(200, body={"id": "$.req.id", "name": "$.fake.FirstName"})
    .build()
)

Java

Mock mock = MockBuilder.grpc("UserService", "GetUser")
    .serverName("grpc-server")
    .condition("$.user_id", AssertAction.EQUALS, "123")
    .respond(200, Map.of("name", "John", "email", "john@test.com"))
    .build();

MCP-мок

Go

mock := mockarty.NewMockBuilder().
    ID("mcp-weather").
    MCPConfig(func(m *mockarty.MCPBuilderCtx) {
        m.Tool("get_weather")
    }).
    Response(func(r *mockarty.ResponseBuilder) {
        r.JSONBody(map[string]any{"temperature": 22, "city": "$.req.city"})
    }).
    Build()

Python

mcp_mock = (
    MockBuilder.mcp("get_weather")
    .id("mcp-weather")
    .respond(200, body={"temperature": 22, "city": "$.req.city"})
    .build()
)

Java

Mock mock = MockBuilder.mcp("search_documents")
    .condition("$.query", AssertAction.NOT_EMPTY, null)
    .respond(200, Map.of("results", List.of("doc1", "doc2")))
    .build();

Прокси и OneOf

Go

// Прокси на реальный бэкенд
proxy := mockarty.NewMockBuilder().
    ID("proxy").
    HTTP(func(h *mockarty.HTTPBuilder) {
        h.Route("/api/external").Method("GET")
    }).
    ProxyTo("https://real-api.example.com").
    Build()

// OneOf ответы (случайные или последовательные)
flaky := mockarty.NewMockBuilder().
    ID("flaky-service").
    HTTP(func(h *mockarty.HTTPBuilder) {
        h.Route("/api/data").Method("GET")
    }).
    OneOfConfig(mockarty.OneOfOrderRandom,
        func(r *mockarty.ResponseBuilder) { r.Status(200).JSONBody("ok") },
        func(r *mockarty.ResponseBuilder) { r.Status(500).Error("boom") },
    ).
    Build()

Python

# Прокси
proxy = (
    MockBuilder.http("/api/external", "GET")
    .id("proxy")
    .proxy_to("https://real-api.example.com")
    .build()
)

Java

// Прокси
Mock proxy = MockBuilder.http("/api/real-service", "GET")
    .proxyTo("https://api.example.com")
    .build();

// OneOf ответы (по порядку)
Mock flaky = MockBuilder.http("/api/flaky", "GET")
    .oneOfOrdered(
        new ContentResponse().statusCode(200).payload(Map.of("status", "ok")),
        new ContentResponse().statusCode(500).error("server error"),
        new ContentResponse().statusCode(200).payload(Map.of("status", "recovered"))
    )
    .build();

Обработка ошибок

REST API Mockarty возвращает единый JSON-конверт для каждой ошибки 4xx/5xx:

{
  "error":      "безопасное человекочитаемое сообщение",
  "code":       "not_found",
  "request_id": "8c3a…d12"
}
  • error — безопасное сообщение, никогда не содержит SQL-строк, стек-трейсов
    или путей. Можно показывать пользователю напрямую.
  • code — стабильный машинный идентификатор из закрытого набора
    (validation, unauthorized, forbidden, not_found, conflict,
    rate_limit, unavailable, external, internal). Используйте именно
    его для ветвления в коде, а не текст сообщения.
  • request_id — серверный correlation ID. Указывайте его в обращениях
    поддержки — оператор найдёт нужную запись в логах.

Каждый SDK разбирает конверт и выбрасывает типизированные исключения /
sentinel ошибки, соответствующие полю code. Старые серверы могут
возвращать только error — SDK и CLI в этом случае откатываются на
маршрутизацию по HTTP-статусу.

Go

import "errors"

_, err := client.Mocks().Get(ctx, "nonexistent")
if errors.Is(err, mockarty.ErrNotFound) {
    // Работает и с новым (code="not_found"), и со старым сервером.
}

var apiErr *mockarty.APIError
if errors.As(err, &apiErr) {
    fmt.Printf("status=%d code=%q msg=%q request_id=%q\n",
        apiErr.StatusCode, apiErr.Code, apiErr.Message, apiErr.RequestID)
}

Python

from mockarty.errors import MockartyNotFoundError, MockartyAPIError

try:
    mock = client.mocks.get("non-existent")
except MockartyNotFoundError as e:
    print(f"мок не найден (request_id={e.request_id})")
except MockartyAPIError as e:
    # e.code — стабильный kind ("validation", "rate_limit", ...).
    # e.request_id — серверный correlation ID.
    print(f"status={e.status_code} code={e.code} msg={e.message} request_id={e.request_id}")

Java

import ru.mockarty.exception.MockartyException;
import ru.mockarty.exception.MockartyNotFoundException;

try {
    client.mocks().get("non-existent");
} catch (MockartyNotFoundException e) {
    System.out.println("Мок не найден");
} catch (MockartyException e) {
    System.out.printf("Ошибка API %d: %s%n", e.getStatusCode(), e.getMessage());
}

Интеграция с тестами

Go: Тестовые хелперы

Go SDK предоставляет методы-хелперы *T, которые автоматически очищают ресурсы по завершении теста:

func TestUserAPI(t *testing.T) {
    client := mockarty.NewClient("http://localhost:5770",
        mockarty.WithAPIKey("mk_test_key"),
    )

    // Создание пространства имён с авто-очисткой
    client.SetupNamespaceT(t, "test-ns")

    // Создание мока с авто-очисткой
    mock := client.CreateMockT(t, mockarty.NewMockBuilder().
        ID("test-user-get").
        Namespace("test-ns").
        HTTP(func(h *mockarty.HTTPBuilder) {
            h.Route("/api/users/1").Method("GET")
        }).
        Response(func(r *mockarty.ResponseBuilder) {
            r.Status(200).JSONBody(map[string]any{"id": "1", "name": "Test"})
        }).
        Build(),
    )
    // мок автоматически удаляется по завершении теста
    _ = mock
}

Python: pytest-фикстуры

pip install mockarty[test]
# conftest.py
pytest_plugins = ["mockarty.testing.fixtures"]

# test_example.py
def test_create_mock(mock_cleanup):
    from mockarty import MockBuilder
    mock = MockBuilder.http("/test", "GET").respond(200, body="ok").build()
    created = mock_cleanup(mock)
    assert created.id is not None

Java: Расширение JUnit 5

import ru.mockarty.junit5.MockartyTest;
import ru.mockarty.junit5.MockartyServer;
import ru.mockarty.MockartyClient;
import ru.mockarty.builder.MockBuilder;

@MockartyTest(namespace = "test", cleanupAfterEach = true)
class UserApiTest {

    @Test
    void shouldReturnUser(MockartyClient client, MockartyServer server) {
        server.createMock(MockBuilder.http("/api/users/1", "GET")
            .respond(200, Map.of("id", 1, "name", "John"))
            .build());

        // Ваш тестовый код...
        // Моки автоматически очищаются после каждого теста
    }
}

Kotlin: DSL в тестах

@MockartyTest(namespace = "test", cleanupAfterEach = true)
class UserApiTest {

    @Test
    fun `should return user`(client: MockartyClient) {
        client.createMock {
            id = "test-user"
            http {
                route = "/api/users/1"
                method = "GET"
            }
            respond {
                statusCode = 200
                body = mapOf("id" to 1, "name" to "John")
            }
        }

        // Проверки здесь...
    }
}

Лучшие практики

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

  2. Устанавливайте TTL для тестовых моков. Всегда устанавливайте ttl для моков, созданных при тестировании, чтобы они автоматически удалялись, даже если teardown не выполнился.

  3. Используйте билдер моков для типобезопасности. Fluent API билдера отлавливает ошибки на этапе компиляции (или в IDE), а не во время выполнения.

  4. Очищайте моки после тестов. Используйте встроенные тестовые хелперы (SetupNamespaceT в Go, mock_cleanup в Python, @MockartyTest в Java/Kotlin) для автоматической очистки.

  5. Используйте хранилища для сценариев с состоянием. Цепочечные хранилища позволяют моделировать многоэтапные процессы (например, создание заказа -> оплата -> доставка) с передачей состояния между вызовами моков.

  6. Предпочитайте переменные окружения для CI/CD. Устанавливайте MOCKARTY_BASE_URL и MOCKARTY_API_KEY в окружении пайплайна вместо жёсткого кодирования значений.

  7. Используйте пакетные операции для настройки. При создании нескольких моков для тестового сценария используйте BatchCreate для сокращения HTTP-запросов.

  8. Версионируйте ваши моки. Используйте API версионирования для отслеживания изменений моков и отката при необходимости.

Устранение проблем

Ошибка Причина Решение
MockartyConnectionError / connection refused Сервер Mockarty не запущен или base_url указывает на неверный хост/порт Проверьте, что curl $MOCKARTY_BASE_URL/health возвращает ok
MockartyUnauthorizedError (401) Отсутствует или неверный API-токен Создайте токен в разделе Админка → API-токены и задайте MOCKARTY_API_KEY
MockartyForbiddenError (403) с invalid license На сервере не активирована лицензия Активируйте лицензионный ключ в Администрировании → Лицензия или обратитесь к администратору
MockartyForbiddenError (403) на namespace Токен ограничен другим пространством имён Используйте токен с доступом к нужному пространству или переключите его в клиенте
MockartyConflictError (409) при create Мок с таким ID уже существует Используйте upsert/update или сгенерируйте уникальный ID
MockartyNotFoundError при разрешении Мок существует, но namespace/route/method не совпадают Проверьте поля мока через list или UI; обратите внимание на слеши и регистр метода
Плейсхолдеры Faker видны в ответе как обычная строка payload отправлен как строка, а не как шаблонный объект Передайте dict/Map со значениями "$.fake.*", а не готовую строку
Запрос зависает без таймаута У клиента по умолчанию нет таймаута Задайте timeout в билдере (например, MockartyClient(timeout=30))
Тесты нестабильно падают при параллельном запуске Несколько тестов используют одно пространство и одинаковые ID моков Используйте отдельное пространство на каждый тестовый набор или рандомизируйте ID

Для специфичных для протокола проблем (gRPC, MCP, Kafka и т. д.) смотрите соответствующий раздел в Справочнике API.

Интеграция с тест-фреймворками

Каждая SDK содержит тонкий framework-слой, который встраивается в ваш
любимый раннер тестов: статус теста, шаги, перехваченные логи и
небольшие вложения автоматически уходят в Mockarty в виде синтетического
case run. Декорируете тест, запускаете — история появляется в UI без
дополнительного клея.

Python — pytest-плагин

После pip install mockarty плагин для pytest подхватится автоматически.
Установите MOCKARTY_BASE_URL и MOCKARTY_API_KEY (или передайте
mockarty_client как фикстуру) и декорируйте нужные тесты:

from mockarty.testing import test_case, attach_report, step, attach

@test_case("CASE-LOGIN-1", plan="qa-smoke")
@attach_report
def test_login(mockarty_client):
    with step("submit form"):
        ...
    with step("verify response"):
        attach("response.json", response.text, content_type="application/json")
        assert response.status_code == 200
  • @test_case("CASE-ID") привязывает тест к существующему кейсу. С
    параметрами auto_create=True и name="..." кейс будет создан при
    первом запуске.
  • @attach_report включает выгрузку результата.
  • with step("...") записывает TCM-шаг. Шаги вкладываются друг в друга.
    Если установлен allure-pytest, шаги дублируются в Allure.
  • attach(name, body, content_type=...) добавляет небольшое вложение,
    которое будет видно в UI рядом с case run.

Java — JUnit 5 extension

Добавьте mockarty-junit5 в test-зависимости и поставьте @MockartyTest
на класс. Привязка к кейсам — на уровне метода:

@MockartyTest(namespace = "qa")
class LoginTest {

    @Test
    @TestCase("CASE-LOGIN-1")
    @AttachReport
    void login(MockartyClient client) {
        try (Step s = Step.open("submit form")) {
            // ...
        }
        Step.run("verify", () -> assertEquals(200, response.status()));
    }
}

Те же примитивы доступны через Step.open(...) / Step.run(...) и
Attachments.attach(...).

Go — хелперы для testing.T

Go SDK даёт хелперы, которые привязывают *testing.T к namespace
Mockarty и чистят созданные моки при выходе теста. Чтобы запустить
Test Plan из Go-теста и проверить итог:

func TestLoginFlow(t *testing.T) {
    c := mockarty.NewClient(os.Getenv("MOCKARTY_BASE_URL"),
        mockarty.WithAPIKey(os.Getenv("MOCKARTY_API_KEY")))
    c.RunPlanT(t, "qa-smoke")  // помечает тест упавшим, если plan-run упал
}

Поведение по умолчанию

Все три framework’а fail-soft: если до сервера Mockarty не достучаться
(нет env, сервер недоступен) — декораторы / аннотации работают как no-op,
и тест выполняется локально. Framework никогда не валит ваш тест
из-за того, что не получилось отчитаться.

Связанная документация