GH GambleHub

Mocking і stubs для інтеграцій

1) Навіщо потрібні моки і заглушки

Інтеграції з платіжними провайдерами, KYC-сервісами, брокерами повідомлень, CRM і т.п. роблять тести повільними, нестабільними і дорогими. Моки/заглушки дозволяють:
  • ізолювати логіку сервісу від нестабільного оточення;
  • детермінувати відповіді та помилки;
  • відтворювати рідкісні граничні кейси (таймаути, 429/5xx, неконсистентність);
  • запускати тести локально і в CI швидко і передбачувано.
💡 Принцип: чим нижче рівень тесту (Unit/Component), тим більше заміщаємо; чим ближче до прод-реальності (Integration/E2E), тим менше заглушок.

2) Терміни і таксономія

Stub - проста заглушка з фіксованим відповіддю, без перевірок взаємодій.
Mock - об'єкт, який очікує виклики і верифікує їх (порядок/кількість/аргументи).
Fake - спрощена реалізація (наприклад, In-Memory репозиторій) з реальною поведінкою.
Spy - обгортка, що записує фактичні виклики.
Service Virtualization - «віртуальний» зовнішній сервіс зі сценаріями, станом і мережевими особливостями.
Record/Replay - запис реального трафіку і подальше відтворення (з фільтрами/редакцією).

Коли що вибирати:
СитуаціяІнструмент
Чиста бізнес-логікаFakes (in-memory), unit-mocks
HTTP-інтеграція з простими кейсамиStubs (WireMock/MockServer/HTTPServer)
Перевірка контракту клієнта до провайдераCDC mocks (Pact)
Складні сценарії/стани/помилки мережіService virtualization / Hoverfly / MockServer
Повідомлення (Kafka/RabbitMQ)Testcontainers + schema-aware продьюсер/консьюмер stubs

3) Архітектурні патерни для тестованості

Ports & Adapters (Hexagonal): виносьте інтеграцію за інтерфейси - легко підмінити на fake/mock.
Антикорупційний шар (ACL): один модуль переводить зовнішню модель в доменну - менше точок мока.
Contract-aware клієнти: генерація по OpenAPI/Protobuf → менше ручних невідповідностей.
Feature-прапори і sandbox-режими: безпечні ключі/ендпоінти для стабу провайдера.

4) HTTP: інструменти та приклади

4. 1 WireMock (standalone/Java DSL)

JSON-стаб:
json
{
"request": { "method": "POST", "urlPath": "/v1/payouts", "headers": { "Idempotency-Key": { "matches": ".+" } } },
"response": {
"status": 201,
"headers": { "Content-Type": "application/json" },
"jsonBody": { "id": "po_123", "status": "queued" },
"fixedDelayMilliseconds": 80
}
}
Java DSL (з перевіркою тіла і варіаціями):
java stubFor(post(urlEqualTo("/v1/payouts"))
.withHeader("Idempotency-Key", matching(".+"))
.withRequestBody(matchingJsonPath("$.amount", equalTo("100. 00")))
.willReturn(aResponse(). withStatus(201). withHeader("Content-Type","application/json")
.withBody("{\"id\":\"po_123\",\"status\":\"queued\"}")));

4. 2 MockServer (динаміка/верифікації)

json
{
"httpRequest": { "method": "GET", "path": "/v1/wallets/w123" },
"httpResponse": { "statusCode": 200, "headers":[{"name":"Content-Type","values":["application/json"]}],
"body": { "id":"w123","currency":"EUR","balance":0 } }
}

4. 3 Hoverfly (middleware, record/replay)

Запишіть трафік проти «пісочниці» провайдера, очистіть PII, зафіксуйте як fixture.
У режимі simulate додайте варіації: 200/4xx/5xx, затримки і «flaky» вікна.

4. 4 Node (Nock) / Python (responses) / Go (`httptest`)

Nock:
js nock('https://psp. example. com')
.post('/v1/payouts'). reply(201, { id:'po_123', status:'queued' })
.post('/v1/payouts'). reply (409, {code: 'duplicate'}) ;//second call - conflict
Go:
go srv:= httptest. NewServer(http. HandlerFunc(func(w http. ResponseWriter, r http. Request){
if r. Header. Get("Idempotency-Key") == "" { w. WriteHeader(400); return }
w. Header(). Set("Content-Type","application/json")
w. WriteHeader(201); w. Write([]byte(`{"id":"po_123","status":"queued"}`))
}))
defer srv. Close()

5) gRPC/Protobuf

5. 1 Генерація стаба

Згенеруйте сервер по'.proto', реалізуйте методи з контрольованими відповідями.
Перевіряйте метадані (headers), статуси ('codes. InvalidArgument`, `codes. DeadlineExceeded`).

Go gRPC fake (фрагмент):
go type FakePayouts struct{ pb. UnimplementedPayoutsServer }
func (f FakePayouts) Create(ctx context. Context, in pb. PayoutReq)(pb. PayoutRes,error){
if in. Amount <= 0 { return nil, status. Error(codes. InvalidArgument,"amount>0") }
return &pb. PayoutRes{Id:"po_123", Status:"QUEUED"}, nil
}

5. 2 grpcurl для негативів


grpcurl -plaintext -d '{"amount":0}' localhost:50051 payouts. Payouts/Create

6) Повідомлення і стріми: Kafka/RabbitMQ

6. 1 Schema-aware моки

Використовуйте Schema Registry і валідуйте Avro/JSON-Schema/Protobuf в тестах.
Producer-тест: повідомлення відповідає схемі; Consumer-тест: приймає стару і нову версії.

6. 2 Testcontainers (приклад Kafka + Registry)

java
KafkaContainer kafka = new KafkaContainer(DockerImageName. parse("confluentinc/cp-kafka:7. 6. 1"));
kafka. start();
//We publish the event and wait for consumption with deduplication by key

6. 3 Негативи

Дублікати, перестановка порядку, затримка доставки, «отруйні» повідомлення (dead-letter).
Великі повідомлення (near-limit), нерозпізнані версії схем.

7) Contract-aware заглушки

7. 1 Pact (CDC mocks)

Consumer формує очікування → pact-файл → provider верифікує на стенді.
Pact stub server відтворює очікування для інтеграційних тестів клієнта.

7. 2 OpenAPI/Protobuf → генерація стабів

Інструменти, які піднімають мок-сервер зі специфікації (в т. ч. Prism, openapi-mock, grpc-mock).
Включайте негативні приклади/коди в специфікацію: це теж контракт.

8) Мережа і хаос: симуляція відмов

Затримки і джиттер: фіксовані/розподілені; перевіряйте дедлайни і пер-try timeout.
Таймаути/розриви: half-open з'єднання, RST, reset потоку H2, 503/Retry-After.
Пакетні втрати/дублікати: для gRPC/стрімів.
Інструменти: Toxiproxy, MockServer (fault injection), xk6-disruptor, netem в CI.

Приклад Toxiproxy (CLI):

toxiproxy-cli toxic add psp --type latency --latency 300 --jitter 100

9) Дані, секрети і детермінізм

Redact та синтетика: ніякого PII в фікстурах; гроші - decimal/суворе форматування.
Фіксація часу: fake clock; «вчора/сьогодні» - контролюйте.
Ідемпотентність: один і той же'Idempotency-Key'→ ту ж відповідь.
Генератори: фабрики/білдери даних з прозорими значеннями (e. g., `test_user_001`).
Версіонуйте фікстури (теги), не зберігайте «зняті» відповіді без медіації.

10) CI/CD і оточення

Матриця: unit (in-process fakes) → component (локальна віртуалізація) → integration (мінімум моків, Testcontainers).
Артефакти: pact-файли, OpenAPI-снапшоти, логи мок-серверів, PCAP при падіннях.
Паралельність: унікальні порти/префікси ключів; Ізоляція контейнерів.
Gate: контракт зелений (CDC verify), специфікація валідна (lint), негативи пройдені.

11) Антипатерни

Моки «копіюють» дефекти реального сервісу → помилкова впевненість. Лікується контрактами і періодичним record/verify.
«Макромокі» цілого світу в кожному тесті → крихкість, дорогий мейнтенанс. Робіть тонкі порти і ACL.
Моки в E2E, де потрібна реальна інтеграція (особливо платежі/вебхуки з HMAC/mTLS).
Флейки через час/рандому/мережевих гонок → використовуйте fake clock, детерміновані сиди.
Секрети у фікстурах/репозиторії. Секрети - тільки через секрет-сховище CI.

12) Специфіка iGaming/фінансів

Платежі/висновки: моки повинні підтримувати'Idempotency-Key','Retry-After', HMAC/mTLS, санкційні коди і «довгі» відповіді.
Бонус-логіка/антифрод: сценарії velocity/429, АТО/челендж, ризик-рішення'allow/deny/challenge'з TTL.
KYC/AML: sandbox-відповіді за рівнями KYC, негативи (mismatch, недійсні документи), вебхуки з anti-replay ('X-Timestamp'вікно).
Юрисдикції/тенанти: обов'язкові заголовки'X-Tenant/X-Region', різні профілі відповідей.

13) Міні-рецепти (шпаргалка)

Повтор платежу: WireMock «Scenarios» - перший'201', другий'409 duplicate'.
Повільний PSP: MockServer'responseDelay'+ перевірка пер-try timeout в клієнті.
Webhooks: локальний HTTP сервер + перевірка підпису HMAC; повтор через 5 сек не створює дубль.
Kafka-дублікати: публікуйте одне й те саме повідомлення двічі; хендлер повинен бути ідемпотентним.
gRPC-статуси: matrix тестов по `codes` (InvalidArgument, DeadlineExceeded, ResourceExhausted).

14) Чек-лист prod-готовності

  • Порти/адаптери виділені; інтеграції приховані за інтерфейсами.
  • Для HTTP/gRPC - є contract-aware стаби (Pact/OpenAPI/Proto) з негативами.
  • Для брокерів - Testcontainers + Registry; тести дублікатів/порядку/великих повідомлень.
  • Хаос: затримки, таймаути, reset, 429/503 з'Retry-After'; мережа емулюється (Toxiproxy/netem).
  • Фікстури без PII; fake clock; ідемпотентність чекається.
  • Матриця CI: unit → component → integration; артефакти логів/контрактів зберігаються.
  • Пісочниці провайдерів: ключі відокремлені, ендпоінти сконфігуровані, є runbook.
  • Record/Replay оновлюється за розкладом, траси редагуються.
  • Метрики flaky і тривалості тестів під контролем; алерти при зростанні.

15) TL; DR

Ізолюйте інтеграції через тонкі порти і використовуйте правильний інструмент під завдання: stubs для простих кейсів, mocks для верифікації взаємодій, fakes для реалістичної поведінки, service virtualization і chaos - для мережевих і рідкісних помилок. Робіть моки контракт-усвідомленими (Pact/OpenAPI/Proto), тримайте фікстури детермінованими і без PII, симулюйте затримки/таймаути/429/5хх. У CI будуйте піраміду: unit → component → integration; реліз блокуйте при червоних контрактах. Для платіжних/КУС-шляхів враховуйте HMAC/mTLS, ідемпотентність і негативні сценарії.

Contact

Зв’яжіться з нами

Звертайтеся з будь-яких питань або за підтримкою.Ми завжди готові допомогти!

Розпочати інтеграцію

Email — обов’язковий. Telegram або WhatsApp — за бажанням.

Ваше ім’я необов’язково
Email необов’язково
Тема необов’язково
Повідомлення необов’язково
Telegram необов’язково
@
Якщо ви вкажете Telegram — ми відповімо й там, додатково до Email.
WhatsApp необов’язково
Формат: +код країни та номер (наприклад, +380XXXXXXXXX).

Натискаючи кнопку, ви погоджуєтесь на обробку даних.