Unit vs Integration Tests
1) Why distinguish between test types
Proper granulation of tests makes development predictable: Unit catch logic defects quickly and cheaply; Integration checks bundles of modules, real transport and "glue." Together, they reduce regression and accelerate releases.
2) Definitions and boundaries
Unit-test
Tests a small unit of behavior (function, class, use-case) in isolation.
External dependencies are replaced (mock/stub/fake).
Fast (ms-tens of ms), deterministic.
Integration test
Checks the interaction of several real components: database, broker (Kafka/RabbitMQ), HTTP/gRPC, file system, cache.
Minimum mocs, real protocols.
Slower (hundreds of ms-seconds), more expensive in support.
3) Testing pyramid (not ice horn)
Foundation: Unit (70-80% in number) - cheap, fast.
Middle layer: Integration/Component (15-25%) - critical paths and contracts.
Top: E2E/UX/Exploratory (5-10%) - minimally enough.
On the sides: Static Analysis/Lint/Type check and Mutation testing as quality amplifiers.
4) What to give Unit and what to Integration
5) Data and fixes
Unit
Inline fictions/builders (factory methods).
Table-driven tests for boundary values.
Property-based approach for invariants (e.g. "sum of debits = sum of credits").
Integration
Hermetic environment: Testcontainers/Docker Compose raise 'postgres + redis + kafka + wiremock'.
Initial seed in the database/cache and cleanup after (transaction/rollback, truncate).
Watches/timers are fake (controlled), otherwise flacks.
6) Tools and patterns
Mocks/Stubs/Fakes/Spies:- Stub is a fixed answer (cheap).
- Mock - check interactions/number of calls.
- Fake is a simplified implementation (for example, In-Memory Repo).
- Contract testing (CDC): Pact/Swagger-based - fix customer expectations and check the provider.
- WireMock/MockServer - HTTP stubs for third-party services.
- Testcontainers are live DBs/brokers locally and in CI without a "zoo."
7) Examples
7. 1 Unit: Payment Idempotence (pseudocode)
python def test_idempotent_create_payment_returns_same_id():
repo = InMemoryPayments()
service = Payments(repo)
first = service. create(amount=100, key="abc")
second = service. create(amount=100, key="abc")
assert first. id == second. id assert repo. count() == 1
7. 2 Integration: Webhook Signature (HMAC) + Repeat
bash docker-compose: app + redis + wiremock (PSP)
docker compose -f docker-compose. test. yml up -d pytest -m "integration and webhook" -q
Test:
- WireMock gives an event with an'X-Timestamp' and a signature.
- The application checks HMAC, deduplicates by 'event _ id', repeat after 5 seconds does not create a double.
- We check '200' and that there is only one entry.
7. 3 CDC: Customer contract to provider
The client generates a Pact (waiting: 'POST/v1/payout' → '201' with a diagram).
The provider in CI runs the verification of the contract at its stand.
8) Speed, parallelism, flakes
Units must run <100 ms per test; packet - seconds.
Integration - parallel by containers/ports; use startup migrations.
- controlled time (fake clock),
- expectations "by explicit event," not 'sleep',
- stable thresholds (retrai with jitter test deterministically).
9) Quality metrics
Coverage (lines/branches): useful for observing the trend, but not the target.
Mutation testing (PIT/Mutmut): Shows whether tests "kill" false changes - the real power of assassins.
Test duration and flaky rate: alerts at growth.
Defect containment: the proportion of bugs intercepted before production.
10) Embedding in CI/CD
Jobs: unit → integration → e2e (fan-out by service).
Dependency cache, parallel matrices by database/language/version.
Reports: JUnit/Allure + container log artifacts (for drops).
Gate: "green unit + critical integration" - merge condition; e2e - on nightly.
yaml strategy:
matrix:
db: [postgres14, postgres16]
steps:
- run: docker run -d --name db -e POSTGRES_PASSWORD=pw postgres:${{ matrix. db }}
- run: pytest -m "unit" -q
- run: pytest -m "integration" -q
11) Microservices and Events
Service contracts: OpenAPI/Protobuf are versioned; compatibility tests (backward).
Event-driven:- Unit: domain event mapping and invariants.
- Integration: publication/subscription in a real broker (Kafka), outbox/inbox semantics, exactly-once imitation (at least - idempotent).
- Out-of-order tests.
12) Data and isolation in Integration
Each test → a unique schema/database (Testcontainers JDBC URL '? TC _ TMPFS =/var/lib/postgresql/data: rw').
Transactional fixes (begin→run→rollback) speed up cleaning.
For Redis/cache, the key prefix is' test: $ {RUN _ ID}: 'and' FLUSHDB'in teardown.
13) Specifics of iGaming/Finance
Money and limits: property-based tests for invariants (balance ≥ 0, total restrictions).
Regulatory: logging check (audit log is written), unchangeable events.
Payments/PSP: HMAC/mTLS integration tests, 'Retry-After', idempotency, dedup 'jti'.
Responsible play: threshold/cooldown rule tests; "vchera→segodnya" on fake clock.
14) Antipatterns
"Units" that raise DB/HTTP are already integration (confuse layers and slow down CI).
High coverage due to empty statements ("covered, but not checked").
Moki logic of third-party services where a contract is needed (breaks when updated).
Tests with 'sleep (5)' instead of event/condition expectations.
Common test database for parallel tests → race and flake.
15) Prod Readiness Checklist
- Pyramid defined as% of Unit/Integration/E2E and target shares by run time.
- Units are isolated, fast, cover boundary values and invariants.
- Integration uses a hermetic environment (Testcontainers/Compose), without common states.
- Contract tests (OpenAPI/Pact) are verified in CI.
- Test data - managed: seed/rollback/prefixes, fake clock.
- Parallel run, JUnit/Allure reports, container log artifacts.
- Metrics: duration, flaky rate, mutation score; alerts to degradation.
- Payment/webhook scenarios: HMAC/mTLS, retrai, idempotency, deadup.
- Strategy documentation and sample test templates.
16) TL; DR
Unit - maximum logic, minimum environment; Integration - minimum moks, maximum realism. Hold the pyramid: Fast Units catch 80% of defects, Integration confirms bundles and contracts. Use hermetic containers, contract tests, fake clock and parallelism. Measure not only coverage, but also mutation score and flaky rate. Especially check the payment/webhook paths: signatures, retrays and idempotency.