<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Patterns on MinimumCD Practice Guide</title><link>https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/</link><description>Recent content in Patterns on MinimumCD Practice Guide</description><generator>Hugo</generator><language>en</language><atom:link href="https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/index.xml" rel="self" type="application/rss+xml"/><item><title>API Provider</title><link>https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/api-provider/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/api-provider/</guid><description>&lt;p&gt;A backend service that exposes an HTTP/gRPC/GraphQL API and owns its own data. No outbound calls to other services in your control.&lt;/p&gt;
&lt;h2 id="what-needs-covered"&gt;What needs covered&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Layer&lt;/th&gt;
 &lt;th&gt;Concern&lt;/th&gt;
 &lt;th&gt;Test type&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Domain logic&lt;/td&gt;
 &lt;td&gt;Business rules, invariants, state transitions&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#solitary-unit-test"&gt;Solitary unit tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Module collaboration&lt;/td&gt;
 &lt;td&gt;Validators + repositories + domain working together&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#sociable-unit-test"&gt;Sociable unit tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Persistence adapter&lt;/td&gt;
 &lt;td&gt;Query correctness, transaction boundaries, migrations against the real DB engine&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#adapter-integration-test"&gt;Adapter integration tests&lt;/a&gt; (testcontainers running production engine and version)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Assembled component&lt;/td&gt;
 &lt;td&gt;Routing, validation, business logic, and persistence wired together through the controller layer&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test"&gt;Component tests&lt;/a&gt; with persistence either real (testcontainers) or doubled (in-memory repository)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Served API&lt;/td&gt;
 &lt;td&gt;What downstream consumers depend on&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#contract-test"&gt;Provider-side contract tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;figure class="figure"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 492"
 role="img" aria-labelledby="apv-title apv-desc"
 font-family="system-ui, -apple-system, sans-serif" font-size="13"&gt;

 &lt;title id="apv-title"&gt;API Provider: layers and the tests that cover each&lt;/title&gt;
 &lt;desc id="apv-desc"&gt;
 Four architectural layers stacked top to bottom. The first three are inside the component boundary; the fourth (the database) is external, drawn with a dashed border. Each layer band shows its name, a one-line description, and the test types that exercise it as small coloured pills. HTTP and API surface is exercised by component tests and provider contract tests. Domain logic is exercised by solitary unit, sociable unit, and component tests. The persistence adapter is exercised by sociable unit, adapter integration, and component tests. The external database is doubled in component tests (in-memory or testcontainer) and used real in adapter integration tests.
 &lt;/desc&gt;

 &lt;rect width="720" height="492" fill="#fafafa" rx="4"/&gt;

 &lt;!-- Title --&gt;
 &lt;text x="360" y="28" text-anchor="middle" font-size="16" font-weight="bold" fill="#1a2a3a"&gt;
 API Provider: Layers and the Tests That Cover Each
 &lt;/text&gt;

 &lt;!-- Inside boundary label --&gt;
 &lt;text x="40" y="56" font-size="10" font-weight="700" letter-spacing="0.08em" fill="#224968"&gt;
 INSIDE THE COMPONENT BOUNDARY
 &lt;/text&gt;

 &lt;!-- Layer 1: HTTP / API surface --&gt;
 &lt;rect x="30" y="68" width="660" height="68" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="68" width="6" height="68" fill="#224968"/&gt;
 &lt;text x="50" y="92" font-size="14" font-weight="700" fill="#1a2a3a"&gt;HTTP / API surface&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="464" y="78" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="504" y="92" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#contract-test" target="_top"&gt;&lt;rect x="550" y="78" width="130" height="20" rx="10" fill="#6c757d"/&gt;&lt;text x="615" y="92" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Provider contract&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="118" font-size="11" font-style="italic" fill="#556"&gt;Routing, auth, validation, status codes&lt;/text&gt;

 &lt;!-- Layer 2: Domain logic --&gt;
 &lt;rect x="30" y="144" width="660" height="68" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="144" width="6" height="68" fill="#224968"/&gt;
 &lt;text x="50" y="168" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Domain logic&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#solitary-unit-test" target="_top"&gt;&lt;rect x="380" y="154" width="104" height="20" rx="10" fill="#4a6741"/&gt;&lt;text x="432" y="168" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Solitary unit&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#sociable-unit-test" target="_top"&gt;&lt;rect x="490" y="154" width="104" height="20" rx="10" fill="#0d7a32"/&gt;&lt;text x="542" y="168" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Sociable unit&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="600" y="154" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="640" y="168" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="194" font-size="11" font-style="italic" fill="#556"&gt;Business rules, invariants, state transitions&lt;/text&gt;

 &lt;!-- Layer 3: Persistence adapter --&gt;
 &lt;rect x="30" y="220" width="660" height="68" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="220" width="6" height="68" fill="#224968"/&gt;
 &lt;text x="50" y="244" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Persistence adapter&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#sociable-unit-test" target="_top"&gt;&lt;rect x="340" y="230" width="104" height="20" rx="10" fill="#0d7a32"/&gt;&lt;text x="392" y="244" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Sociable unit&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#adapter-integration-test" target="_top"&gt;&lt;rect x="450" y="230" width="144" height="20" rx="10" fill="#30648e"/&gt;&lt;text x="522" y="244" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Adapter integration&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="600" y="230" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="640" y="244" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="270" font-size="11" font-style="italic" fill="#556"&gt;Queries, transactions, migrations&lt;/text&gt;

 &lt;!-- Boundary divider --&gt;
 &lt;line x1="40" y1="306" x2="680" y2="306" stroke="#94a3b8" stroke-width="1" stroke-dasharray="6,4"/&gt;
 &lt;text x="360" y="302" text-anchor="middle" font-size="10" fill="#556" font-weight="600" font-style="italic"&gt;component boundary&lt;/text&gt;

 &lt;!-- Outside boundary label --&gt;
 &lt;text x="40" y="324" font-size="10" font-weight="700" letter-spacing="0.08em" fill="#6c757d"&gt;
 OUTSIDE THE BOUNDARY
 &lt;/text&gt;

 &lt;!-- Layer 4: Database (external) --&gt;
 &lt;rect x="30" y="336" width="660" height="68" rx="6" fill="#edf2f7" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="6,4"/&gt;
 &lt;rect x="30" y="336" width="6" height="68" fill="#6c757d"/&gt;
 &lt;text x="50" y="360" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Database (external)&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="450" y="346" width="80" height="20" rx="10" fill="none" stroke="#224968" stroke-width="2"/&gt;&lt;text x="490" y="360" text-anchor="middle" font-size="11" font-weight="600" fill="#224968"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#adapter-integration-test" target="_top"&gt;&lt;rect x="536" y="346" width="144" height="20" rx="10" fill="#30648e"/&gt;&lt;text x="608" y="360" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Adapter integration&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="386" font-size="11" font-style="italic" fill="#556"&gt;Production engine. Doubled in component tests; real in adapter integration tests.&lt;/text&gt;

 &lt;!-- Legend --&gt;
 &lt;rect x="40" y="434" width="30" height="16" rx="3" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="40" y="434" width="4" height="16" fill="#224968"/&gt;
 &lt;text x="78" y="446" font-size="11" fill="#556"&gt;internal layer&lt;/text&gt;
 &lt;rect x="320" y="435" width="24" height="14" rx="7" fill="#224968"/&gt;
 &lt;text x="354" y="446" font-size="11" fill="#556"&gt;real code under test&lt;/text&gt;

 &lt;rect x="40" y="458" width="30" height="16" rx="3" fill="#edf2f7" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4,3"/&gt;
 &lt;rect x="40" y="458" width="4" height="16" fill="#6c757d"/&gt;
 &lt;text x="78" y="470" font-size="11" fill="#556"&gt;external (dashed border)&lt;/text&gt;
 &lt;rect x="320" y="459" width="24" height="14" rx="7" fill="none" stroke="#224968" stroke-width="2"/&gt;
 &lt;text x="354" y="470" font-size="11" fill="#556"&gt;doubled in this test&lt;/text&gt;

&lt;/svg&gt;
&lt;figcaption class="figure-caption sr-only"&gt;Layered diagram of an API provider showing four architectural layers stacked top to bottom. The first three are inside the component boundary: HTTP and API surface (covered by component tests and provider contract tests), domain logic (covered by solitary unit, sociable unit, and component tests), and persistence adapter (covered by sociable unit, adapter integration, and component tests). Below the dashed component boundary, the external database is doubled in component tests (in-memory or testcontainer) and used real in adapter integration tests against the production engine.&lt;/figcaption&gt;&lt;/figure&gt;

&lt;h2 id="positive-test-cases"&gt;Positive test cases&lt;/h2&gt;
&lt;p&gt;Common cases to consider, not an exhaustive list. Drop items that don&amp;rsquo;t apply and add ones the pattern doesn&amp;rsquo;t mention but your component needs.&lt;/p&gt;</description></item><item><title>API Consumer</title><link>https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/api-consumer/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/api-consumer/</guid><description>&lt;p&gt;Same as &lt;a href="https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/api-provider/"&gt;API provider&lt;/a&gt;, plus outbound HTTP/gRPC calls to services the team does not own (or does own but deploys independently). This is the most failure-prone pattern in distributed systems and gets the most testing attention.&lt;/p&gt;
&lt;h2 id="what-needs-covered"&gt;What needs covered&lt;/h2&gt;
&lt;p&gt;Everything from the &lt;a href="https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/api-provider/"&gt;API provider&lt;/a&gt; pattern, plus:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Layer&lt;/th&gt;
 &lt;th&gt;Concern&lt;/th&gt;
 &lt;th&gt;Test type&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Outbound HTTP client&lt;/td&gt;
 &lt;td&gt;Request shape, response parsing, status code handling, header propagation, timeout enforcement&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#adapter-integration-test"&gt;Adapter integration tests&lt;/a&gt; (against WireMock or, periodically, the real downstream)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Consumed API contract&lt;/td&gt;
 &lt;td&gt;The fields and status codes the consumer depends on&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#contract-test"&gt;Consumer-side contract tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Resilience under degraded &lt;a href="https://beyond.minimumcd.org/docs/reference/glossary/#dependency"&gt;dependencies&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Retries, circuit breaking, backoff, fallback, partial-failure compensation&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test"&gt;Component tests&lt;/a&gt; with fault-injecting client doubles&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Composite behavior&lt;/td&gt;
 &lt;td&gt;The service still returns useful responses when downstreams misbehave&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test"&gt;Component tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;figure class="figure"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 692"
 role="img" aria-labelledby="apc-title apc-desc"
 font-family="system-ui, -apple-system, sans-serif" font-size="13"&gt;

 &lt;title id="apc-title"&gt;API Consumer: layers and the tests that cover each&lt;/title&gt;
 &lt;desc id="apc-desc"&gt;
 Seven architectural layers stacked top to bottom. The first five (HTTP and API surface, domain logic and orchestration, resilience policy, outbound HTTP client, and persistence adapter) are inside the component boundary. Below the dashed component boundary, the external database and the external downstream service are drawn with dashed borders. Each band shows its name, a one-line description, and the test types that exercise it as small coloured pills. Component tests cover all internal layers including resilience, with both downstream service and database doubled. Adapter integration tests pin the outbound and persistence protocols against real containers. Consumer contract tests pin the outbound boundary. Out-of-band integration tests exercise the real downstream service to confirm doubles still match reality.
 &lt;/desc&gt;

 &lt;rect width="720" height="692" fill="#fafafa" rx="4"/&gt;

 &lt;!-- Title --&gt;
 &lt;text x="360" y="28" text-anchor="middle" font-size="16" font-weight="bold" fill="#1a2a3a"&gt;
 API Consumer: Layers and the Tests That Cover Each
 &lt;/text&gt;

 &lt;!-- Inside boundary label --&gt;
 &lt;text x="40" y="56" font-size="10" font-weight="700" letter-spacing="0.08em" fill="#224968"&gt;
 INSIDE THE COMPONENT BOUNDARY
 &lt;/text&gt;

 &lt;!-- Layer 1: HTTP / API surface (inbound) --&gt;
 &lt;rect x="30" y="68" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="68" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="92" font-size="14" font-weight="700" fill="#1a2a3a"&gt;HTTP / API surface (inbound)&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="600" y="78" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="640" y="92" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="116" font-size="11" font-style="italic" fill="#556"&gt;Routing, auth, validation&lt;/text&gt;

 &lt;!-- Layer 2: Domain logic and orchestration --&gt;
 &lt;rect x="30" y="136" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="136" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="160" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Domain logic and orchestration&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#solitary-unit-test" target="_top"&gt;&lt;rect x="380" y="146" width="104" height="20" rx="10" fill="#4a6741"/&gt;&lt;text x="432" y="160" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Solitary unit&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#sociable-unit-test" target="_top"&gt;&lt;rect x="490" y="146" width="104" height="20" rx="10" fill="#0d7a32"/&gt;&lt;text x="542" y="160" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Sociable unit&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="600" y="146" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="640" y="160" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="184" font-size="11" font-style="italic" fill="#556"&gt;Composes calls, computes results&lt;/text&gt;

 &lt;!-- Layer 3: Resilience policy --&gt;
 &lt;rect x="30" y="204" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="204" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="228" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Resilience policy&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="600" y="214" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="640" y="228" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="252" font-size="11" font-style="italic" fill="#556"&gt;Retry, circuit breaker, timeout, fallback&lt;/text&gt;

 &lt;!-- Layer 4: Outbound HTTP client --&gt;
 &lt;rect x="30" y="272" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="272" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="296" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Outbound HTTP client&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#adapter-integration-test" target="_top"&gt;&lt;rect x="346" y="282" width="144" height="20" rx="10" fill="#30648e"/&gt;&lt;text x="418" y="296" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Adapter integration&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#contract-test" target="_top"&gt;&lt;rect x="496" y="282" width="124" height="20" rx="10" fill="#6c757d"/&gt;&lt;text x="558" y="296" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Consumer contract&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="626" y="282" width="54" height="20" rx="10" fill="#224968"/&gt;&lt;text x="653" y="296" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Comp.&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="320" font-size="11" font-style="italic" fill="#556"&gt;Request build, response parse, headers, deadlines&lt;/text&gt;

 &lt;!-- Layer 5: Persistence adapter --&gt;
 &lt;rect x="30" y="340" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="340" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="364" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Persistence adapter&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#sociable-unit-test" target="_top"&gt;&lt;rect x="490" y="350" width="104" height="20" rx="10" fill="#0d7a32"/&gt;&lt;text x="542" y="364" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Sociable unit&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="600" y="350" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="640" y="364" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="388" font-size="11" font-style="italic" fill="#556"&gt;Queries, transactions, migrations&lt;/text&gt;

 &lt;!-- Boundary divider --&gt;
 &lt;line x1="40" y1="418" x2="680" y2="418" stroke="#94a3b8" stroke-width="1" stroke-dasharray="6,4"/&gt;
 &lt;text x="360" y="414" text-anchor="middle" font-size="10" fill="#556" font-weight="600" font-style="italic"&gt;component boundary&lt;/text&gt;

 &lt;!-- Outside boundary label --&gt;
 &lt;text x="40" y="436" font-size="10" font-weight="700" letter-spacing="0.08em" fill="#6c757d"&gt;
 OUTSIDE THE BOUNDARY
 &lt;/text&gt;

 &lt;!-- Layer 6: Database (external) --&gt;
 &lt;rect x="30" y="448" width="660" height="60" rx="6" fill="#edf2f7" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="6,4"/&gt;
 &lt;rect x="30" y="448" width="6" height="60" fill="#6c757d"/&gt;
 &lt;text x="50" y="472" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Database (external)&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="490" y="458" width="80" height="20" rx="10" fill="none" stroke="#224968" stroke-width="2"/&gt;&lt;text x="530" y="472" text-anchor="middle" font-size="11" font-weight="600" fill="#224968"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#adapter-integration-test" target="_top"&gt;&lt;rect x="576" y="458" width="104" height="20" rx="10" fill="#30648e"/&gt;&lt;text x="628" y="472" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Adapter integ.&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="496" font-size="11" font-style="italic" fill="#556"&gt;Production engine. Doubled in component; real in adapter integration.&lt;/text&gt;

 &lt;!-- Layer 7: Downstream service (external) --&gt;
 &lt;rect x="30" y="516" width="660" height="80" rx="6" fill="#edf2f7" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="6,4"/&gt;
 &lt;rect x="30" y="516" width="6" height="80" fill="#6c757d"/&gt;
 &lt;text x="50" y="540" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Downstream service (external)&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="370" y="550" width="80" height="20" rx="10" fill="none" stroke="#224968" stroke-width="2"/&gt;&lt;text x="410" y="564" text-anchor="middle" font-size="11" font-weight="600" fill="#224968"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#adapter-integration-test" target="_top"&gt;&lt;rect x="456" y="550" width="120" height="20" rx="10" fill="none" stroke="#30648e" stroke-width="2"/&gt;&lt;text x="516" y="564" text-anchor="middle" font-size="11" font-weight="600" fill="#30648e"&gt;Adapter integ.&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#out-of-band-test" target="_top"&gt;&lt;rect x="582" y="550" width="98" height="20" rx="10" fill="#6c757d"/&gt;&lt;text x="631" y="564" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;OOB integration&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="582" font-size="11" font-style="italic" fill="#556"&gt;Third-party or in-house API. Doubled in pipeline tests; OOB integration uses the real downstream on a schedule.&lt;/text&gt;

 &lt;!-- Legend --&gt;
 &lt;rect x="40" y="634" width="30" height="16" rx="3" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="40" y="634" width="4" height="16" fill="#224968"/&gt;
 &lt;text x="78" y="646" font-size="11" fill="#556"&gt;internal layer&lt;/text&gt;
 &lt;rect x="320" y="635" width="24" height="14" rx="7" fill="#224968"/&gt;
 &lt;text x="354" y="646" font-size="11" fill="#556"&gt;real code under test&lt;/text&gt;

 &lt;rect x="40" y="658" width="30" height="16" rx="3" fill="#edf2f7" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4,3"/&gt;
 &lt;rect x="40" y="658" width="4" height="16" fill="#6c757d"/&gt;
 &lt;text x="78" y="670" font-size="11" fill="#556"&gt;external (dashed border)&lt;/text&gt;
 &lt;rect x="320" y="659" width="24" height="14" rx="7" fill="none" stroke="#224968" stroke-width="2"/&gt;
 &lt;text x="354" y="670" font-size="11" fill="#556"&gt;doubled in this test&lt;/text&gt;

&lt;/svg&gt;
&lt;figcaption class="figure-caption sr-only"&gt;Layered diagram of an API consumer with seven architectural layers. The first five (HTTP and API surface, domain logic and orchestration, resilience policy, outbound HTTP client, persistence adapter) are inside the component boundary. Below the dashed boundary, the external database and the external downstream service are drawn with dashed borders. Component tests cover every internal layer including resilience, with both database and downstream service doubled. Adapter integration tests pin the outbound and persistence protocols against real containers. Consumer contract tests pin the outbound boundary. Out-of-band integration tests exercise the real downstream service to confirm doubles still match reality.&lt;/figcaption&gt;&lt;/figure&gt;

&lt;h2 id="positive-test-cases"&gt;Positive test cases&lt;/h2&gt;
&lt;p&gt;Common cases to consider, not an exhaustive list. Drop items that don&amp;rsquo;t apply and add ones the pattern doesn&amp;rsquo;t mention but your component needs.&lt;/p&gt;</description></item><item><title>Scheduled Job</title><link>https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/scheduled-job/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/scheduled-job/</guid><description>&lt;p&gt;A job that runs on a cron, queue, or external scheduler. Reads from data sources, writes reports or updates state. Often has no inbound API surface. The entrypoint is the scheduler.&lt;/p&gt;
&lt;p&gt;This pattern has two test design challenges that the &lt;a href="https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/api-provider/"&gt;API provider&lt;/a&gt; and &lt;a href="https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/api-consumer/"&gt;API consumer&lt;/a&gt; patterns don&amp;rsquo;t have: time and data volume.&lt;/p&gt;
&lt;h2 id="what-needs-covered"&gt;What needs covered&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Layer&lt;/th&gt;
 &lt;th&gt;Concern&lt;/th&gt;
 &lt;th&gt;Test type&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Pure transformation logic&lt;/td&gt;
 &lt;td&gt;The data calculation itself, with no I/O&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#solitary-unit-test"&gt;Solitary unit tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Source and sink adapters&lt;/td&gt;
 &lt;td&gt;Reading from sources, writing to sinks: protocol correctness, error mapping&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#adapter-integration-test"&gt;Adapter integration tests&lt;/a&gt; against real source/sink containers or WireMock&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Job orchestration&lt;/td&gt;
 &lt;td&gt;Idempotency, partial failure recovery, checkpointing, locking, time-window logic&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test"&gt;Component tests&lt;/a&gt; through the job&amp;rsquo;s invocation entrypoint, with client doubles, source/sink doubles, and an injected clock&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Process startup&lt;/td&gt;
 &lt;td&gt;Exit codes, signal handling, configuration loading, real environment wiring&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#deployed-binary-test"&gt;Deployed-binary tests&lt;/a&gt; that invoke the real &lt;a href="https://beyond.minimumcd.org/docs/reference/glossary/#artifact"&gt;artifact&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Scheduling integration&lt;/td&gt;
 &lt;td&gt;The scheduler triggers the right entrypoint with the right arguments, environment, secrets, and concurrency settings&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#out-of-band-test"&gt;Out-of-band integration check&lt;/a&gt; against the real scheduler in a non-prod environment&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Observability&lt;/td&gt;
 &lt;td&gt;Job ran, succeeded/failed, duration, records processed, error count&lt;/td&gt;
 &lt;td&gt;Assertions in &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test"&gt;component tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;figure class="figure"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 632"
 role="img" aria-labelledby="sjc-title sjc-desc"
 font-family="system-ui, -apple-system, sans-serif" font-size="13"&gt;

 &lt;title id="sjc-title"&gt;Scheduled Job: layers and the tests that cover each&lt;/title&gt;
 &lt;desc id="sjc-desc"&gt;
 Six architectural layers stacked top to bottom. The first four (pure transformation logic, job orchestration, source and sink adapters, and process startup) are inside the component boundary. Below the dashed component boundary, the external source and sink and the external scheduler and system clock are drawn with dashed borders. Each band shows its name, a one-line description, and the test types that exercise it as small coloured pills. Solitary unit tests cover pure transformation. Component tests cover orchestration with the clock and gateways doubled. Adapter integration tests pin source and sink protocols against real containers. Deployed-binary tests cover process startup on the actual artifact. Out-of-band integration uses the real scheduler and clock on a schedule.
 &lt;/desc&gt;

 &lt;rect width="720" height="632" fill="#fafafa" rx="4"/&gt;

 &lt;!-- Title --&gt;
 &lt;text x="360" y="28" text-anchor="middle" font-size="16" font-weight="bold" fill="#1a2a3a"&gt;
 Scheduled Job: Layers and the Tests That Cover Each
 &lt;/text&gt;

 &lt;!-- Inside boundary label --&gt;
 &lt;text x="40" y="56" font-size="10" font-weight="700" letter-spacing="0.08em" fill="#224968"&gt;
 INSIDE THE COMPONENT BOUNDARY
 &lt;/text&gt;

 &lt;!-- Layer 1: Pure transformation logic --&gt;
 &lt;rect x="30" y="68" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="68" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="92" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Pure transformation logic&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#solitary-unit-test" target="_top"&gt;&lt;rect x="490" y="78" width="104" height="20" rx="10" fill="#4a6741"/&gt;&lt;text x="542" y="92" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Solitary unit&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="600" y="78" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="640" y="92" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="116" font-size="11" font-style="italic" fill="#556"&gt;The data calculation, no I/O&lt;/text&gt;

 &lt;!-- Layer 2: Job orchestration --&gt;
 &lt;rect x="30" y="136" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="136" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="160" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Job orchestration&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="600" y="146" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="640" y="160" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="184" font-size="11" font-style="italic" fill="#556"&gt;Idempotency, locking, time windows, checkpointing (injected clock)&lt;/text&gt;

 &lt;!-- Layer 3: Source and sink adapters --&gt;
 &lt;rect x="30" y="204" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="204" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="228" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Source and sink adapters&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#adapter-integration-test" target="_top"&gt;&lt;rect x="346" y="214" width="144" height="20" rx="10" fill="#30648e"/&gt;&lt;text x="418" y="228" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Adapter integration&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#contract-test" target="_top"&gt;&lt;rect x="496" y="214" width="124" height="20" rx="10" fill="#6c757d"/&gt;&lt;text x="558" y="228" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Source/sink contract&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="626" y="214" width="54" height="20" rx="10" fill="#224968"/&gt;&lt;text x="653" y="228" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Comp.&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="252" font-size="11" font-style="italic" fill="#556"&gt;Protocol, error mapping, transactional semantics&lt;/text&gt;

 &lt;!-- Layer 4: Process startup --&gt;
 &lt;rect x="30" y="272" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="272" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="296" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Process startup&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#deployed-binary-test" target="_top"&gt;&lt;rect x="560" y="282" width="120" height="20" rx="10" fill="#224968"/&gt;&lt;text x="620" y="296" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Deployed binary&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="320" font-size="11" font-style="italic" fill="#556"&gt;Exit codes, signal handling, config and secret loading, lock acquisition&lt;/text&gt;

 &lt;!-- Boundary divider --&gt;
 &lt;line x1="40" y1="354" x2="680" y2="354" stroke="#94a3b8" stroke-width="1" stroke-dasharray="6,4"/&gt;
 &lt;text x="360" y="350" text-anchor="middle" font-size="10" fill="#556" font-weight="600" font-style="italic"&gt;component boundary&lt;/text&gt;

 &lt;!-- Outside boundary label --&gt;
 &lt;text x="40" y="372" font-size="10" font-weight="700" letter-spacing="0.08em" fill="#6c757d"&gt;
 OUTSIDE THE BOUNDARY
 &lt;/text&gt;

 &lt;!-- Layer 5: External source and sink --&gt;
 &lt;rect x="30" y="384" width="660" height="80" rx="6" fill="#edf2f7" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="6,4"/&gt;
 &lt;rect x="30" y="384" width="6" height="80" fill="#6c757d"/&gt;
 &lt;text x="50" y="408" font-size="14" font-weight="700" fill="#1a2a3a"&gt;External source and sink&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="386" y="418" width="80" height="20" rx="10" fill="none" stroke="#224968" stroke-width="2"/&gt;&lt;text x="426" y="432" text-anchor="middle" font-size="11" font-weight="600" fill="#224968"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#adapter-integration-test" target="_top"&gt;&lt;rect x="472" y="418" width="120" height="20" rx="10" fill="#30648e"/&gt;&lt;text x="532" y="432" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Adapter integ.&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#out-of-band-test" target="_top"&gt;&lt;rect x="598" y="418" width="82" height="20" rx="10" fill="#6c757d"/&gt;&lt;text x="639" y="432" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;OOB integ.&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="450" font-size="11" font-style="italic" fill="#556"&gt;Data store, broker, file system. Doubled in component; real in adapter integration.&lt;/text&gt;

 &lt;!-- Layer 6: External scheduler and system clock --&gt;
 &lt;rect x="30" y="472" width="660" height="80" rx="6" fill="#edf2f7" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="6,4"/&gt;
 &lt;rect x="30" y="472" width="6" height="80" fill="#6c757d"/&gt;
 &lt;text x="50" y="496" font-size="14" font-weight="700" fill="#1a2a3a"&gt;External scheduler and system clock&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="476" y="506" width="120" height="20" rx="10" fill="none" stroke="#224968" stroke-width="2"/&gt;&lt;text x="536" y="520" text-anchor="middle" font-size="11" font-weight="600" fill="#224968"&gt;Component (clock doubled)&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#out-of-band-test" target="_top"&gt;&lt;rect x="602" y="506" width="78" height="20" rx="10" fill="#6c757d"/&gt;&lt;text x="641" y="520" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;OOB integ.&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="538" font-size="11" font-style="italic" fill="#556"&gt;Cron expression, env, secrets, time-zone wiring. OOB check uses the real scheduler and clock.&lt;/text&gt;

 &lt;!-- Legend --&gt;
 &lt;rect x="40" y="582" width="30" height="16" rx="3" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="40" y="582" width="4" height="16" fill="#224968"/&gt;
 &lt;text x="78" y="594" font-size="11" fill="#556"&gt;internal layer&lt;/text&gt;
 &lt;rect x="320" y="583" width="24" height="14" rx="7" fill="#224968"/&gt;
 &lt;text x="354" y="594" font-size="11" fill="#556"&gt;real code under test&lt;/text&gt;

 &lt;rect x="40" y="606" width="30" height="16" rx="3" fill="#edf2f7" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4,3"/&gt;
 &lt;rect x="40" y="606" width="4" height="16" fill="#6c757d"/&gt;
 &lt;text x="78" y="618" font-size="11" fill="#556"&gt;external (dashed border)&lt;/text&gt;
 &lt;rect x="320" y="607" width="24" height="14" rx="7" fill="none" stroke="#224968" stroke-width="2"/&gt;
 &lt;text x="354" y="618" font-size="11" fill="#556"&gt;doubled in this test&lt;/text&gt;

&lt;/svg&gt;
&lt;figcaption class="figure-caption sr-only"&gt;Layered diagram of a scheduled job with six architectural layers. The first four (pure transformation logic, job orchestration, source and sink gateways, process startup) are inside the component boundary. Below the dashed boundary, the external source and sink and the external scheduler and system clock are drawn with dashed borders. Solitary unit tests cover pure transformation. Component tests cover orchestration with the clock and gateways doubled. Adapter integration tests pin source and sink protocols against real containers. Deployed-binary tests cover process startup on the actual artifact the scheduler will invoke. Out-of-band integration uses the real scheduler and clock on a schedule.&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Process startup matters more here than for an API service, because scheduled jobs typically have non-trivial startup behavior (config loading, secret resolution, lock acquisition) that a component test with the SUT in-memory can bypass. The right shape is many component tests for behavior, plus one or two tests that invoke the actual deployed binary the scheduler will invoke.&lt;/p&gt;</description></item><item><title>User Interface</title><link>https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/user-interface/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/user-interface/</guid><description>&lt;p&gt;A UI that renders data and accepts user interaction. Talks to one or more backend APIs.&lt;/p&gt;
&lt;h2 id="what-needs-covered"&gt;What needs covered&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Layer&lt;/th&gt;
 &lt;th&gt;Concern&lt;/th&gt;
 &lt;th&gt;Test type&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Pure rendering&lt;/td&gt;
 &lt;td&gt;Component renders given props/state&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#solitary-unit-test"&gt;Solitary unit tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Component composition&lt;/td&gt;
 &lt;td&gt;Composed components wire correctly&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#sociable-unit-test"&gt;Sociable unit tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Feature behavior&lt;/td&gt;
 &lt;td&gt;A flow (login, checkout, search) works through the rendered DOM with the backend stubbed at the network layer&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test"&gt;Component tests&lt;/a&gt; driven by Playwright with the team&amp;rsquo;s unit-testing framework as the runner&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Backend contract&lt;/td&gt;
 &lt;td&gt;What the UI sends and expects from each backend endpoint&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#contract-test"&gt;Consumer-side contract tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;End-to-end happy paths&lt;/td&gt;
 &lt;td&gt;A small number of critical journeys against real backends&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/test-types/e2e/"&gt;E2E tests&lt;/a&gt;, post-deploy&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Visual regression&lt;/td&gt;
 &lt;td&gt;The UI looks right&lt;/td&gt;
 &lt;td&gt;Snapshot or visual diff tests&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Accessibility&lt;/td&gt;
 &lt;td&gt;The UI works for assistive tech and keyboard users&lt;/td&gt;
 &lt;td&gt;Assertions in &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test"&gt;component tests&lt;/a&gt; + automated WCAG scanning&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;figure class="figure"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 532"
 role="img" aria-labelledby="uic-title uic-desc"
 font-family="system-ui, -apple-system, sans-serif" font-size="13"&gt;

 &lt;title id="uic-title"&gt;User Interface: layers and the tests that cover each&lt;/title&gt;
 &lt;desc id="uic-desc"&gt;
 Five architectural layers stacked top to bottom. The first four (pure rendering, component composition, feature behaviour in the rendered DOM, and backend HTTP client) are inside the component boundary. Below the dashed component boundary, the external backend API is drawn with a dashed border. Each band shows its name, a one-line description, and the test types that exercise it as small coloured pills. Solitary unit tests cover pure rendering. Sociable unit tests cover composition. Component tests driven by Playwright with the team's unit-testing framework cover feature behaviour with the backend stubbed at the network layer. Consumer contract tests pin each backend boundary. End-to-end tests run post-deploy in a real browser against the real backend.
 &lt;/desc&gt;

 &lt;rect width="720" height="532" fill="#fafafa" rx="4"/&gt;

 &lt;!-- Title --&gt;
 &lt;text x="360" y="28" text-anchor="middle" font-size="16" font-weight="bold" fill="#1a2a3a"&gt;
 User Interface: Layers and the Tests That Cover Each
 &lt;/text&gt;

 &lt;!-- Inside boundary label --&gt;
 &lt;text x="40" y="56" font-size="10" font-weight="700" letter-spacing="0.08em" fill="#224968"&gt;
 INSIDE THE COMPONENT BOUNDARY
 &lt;/text&gt;

 &lt;!-- Layer 1: Pure rendering --&gt;
 &lt;rect x="30" y="68" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="68" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="92" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Pure rendering&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#solitary-unit-test" target="_top"&gt;&lt;rect x="488" y="78" width="104" height="20" rx="10" fill="#4a6741"/&gt;&lt;text x="540" y="92" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Solitary unit&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="600" y="78" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="640" y="92" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="116" font-size="11" font-style="italic" fill="#556"&gt;A component renders given props or state&lt;/text&gt;

 &lt;!-- Layer 2: Component composition --&gt;
 &lt;rect x="30" y="136" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="136" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="160" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Component composition&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#sociable-unit-test" target="_top"&gt;&lt;rect x="488" y="146" width="104" height="20" rx="10" fill="#0d7a32"/&gt;&lt;text x="540" y="160" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Sociable unit&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="600" y="146" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="640" y="160" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="184" font-size="11" font-style="italic" fill="#556"&gt;Composed components wire correctly&lt;/text&gt;

 &lt;!-- Layer 3: Feature behaviour in the DOM --&gt;
 &lt;rect x="30" y="204" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="204" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="228" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Feature behaviour in the rendered DOM&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="600" y="214" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="640" y="228" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="252" font-size="11" font-style="italic" fill="#556"&gt;Flows, form validation, a11y assertions, error UX (Playwright + unit-test framework)&lt;/text&gt;

 &lt;!-- Layer 4: Backend HTTP client --&gt;
 &lt;rect x="30" y="272" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="272" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="296" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Backend HTTP client&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="448" y="282" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="488" y="296" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#contract-test" target="_top"&gt;&lt;rect x="534" y="282" width="146" height="20" rx="10" fill="#6c757d"/&gt;&lt;text x="607" y="296" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Consumer contract&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="320" font-size="11" font-style="italic" fill="#556"&gt;Fetch / request build, response parse, retry, auth headers&lt;/text&gt;

 &lt;!-- Boundary divider --&gt;
 &lt;line x1="40" y1="354" x2="680" y2="354" stroke="#94a3b8" stroke-width="1" stroke-dasharray="6,4"/&gt;
 &lt;text x="360" y="350" text-anchor="middle" font-size="10" fill="#556" font-weight="600" font-style="italic"&gt;component boundary&lt;/text&gt;

 &lt;!-- Outside boundary label --&gt;
 &lt;text x="40" y="372" font-size="10" font-weight="700" letter-spacing="0.08em" fill="#6c757d"&gt;
 OUTSIDE THE BOUNDARY
 &lt;/text&gt;

 &lt;!-- Layer 5: Backend API (external) --&gt;
 &lt;rect x="30" y="384" width="660" height="80" rx="6" fill="#edf2f7" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="6,4"/&gt;
 &lt;rect x="30" y="384" width="6" height="80" fill="#6c757d"/&gt;
 &lt;text x="50" y="408" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Backend API (external)&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="490" y="418" width="80" height="20" rx="10" fill="none" stroke="#224968" stroke-width="2"/&gt;&lt;text x="530" y="432" text-anchor="middle" font-size="11" font-weight="600" fill="#224968"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/test-types/e2e/" target="_top"&gt;&lt;rect x="606" y="418" width="74" height="20" rx="10" fill="#6c757d"/&gt;&lt;text x="643" y="432" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;E2E&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="450" font-size="11" font-style="italic" fill="#556"&gt;Stubbed via page.route in component tests; real in E2E smoke.&lt;/text&gt;

 &lt;!-- Legend --&gt;
 &lt;rect x="40" y="494" width="30" height="16" rx="3" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="40" y="494" width="4" height="16" fill="#224968"/&gt;
 &lt;text x="78" y="506" font-size="11" fill="#556"&gt;internal layer&lt;/text&gt;
 &lt;rect x="320" y="495" width="24" height="14" rx="7" fill="#224968"/&gt;
 &lt;text x="354" y="506" font-size="11" fill="#556"&gt;real code under test&lt;/text&gt;

 &lt;rect x="40" y="518" width="30" height="16" rx="3" fill="#edf2f7" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4,3"/&gt;
 &lt;rect x="40" y="518" width="4" height="16" fill="#6c757d"/&gt;
 &lt;text x="78" y="530" font-size="11" fill="#556"&gt;external (dashed border)&lt;/text&gt;
 &lt;rect x="320" y="519" width="24" height="14" rx="7" fill="none" stroke="#224968" stroke-width="2"/&gt;
 &lt;text x="354" y="530" font-size="11" fill="#556"&gt;doubled in this test&lt;/text&gt;

&lt;/svg&gt;
&lt;figcaption class="figure-caption sr-only"&gt;Layered diagram of a user interface with five architectural layers. The first four (pure rendering, component composition, feature behavior in the rendered DOM, backend HTTP client) are inside the component boundary. Below the dashed boundary, the external backend API is drawn with a dashed border. Solitary unit tests cover pure rendering. Sociable unit tests cover composition. Component tests driven by Playwright cover feature behavior with the backend doubled at the network layer. Consumer contract tests pin each backend boundary. End-to-end tests run post-deploy against the real backend.&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;UI component tests run in a real browser engine (Chromium, Firefox, WebKit) driven by Playwright, with the team&amp;rsquo;s existing unit-testing framework (Vitest, Jest, or whatever is already in the project) as the runner. In-memory renderer shortcuts like JSDOM are rejected: they trade accuracy for speed and produce false greens around layout, focus, event timing, Intersection Observer, and animations - exactly the surface where UI bugs live. Playwright&amp;rsquo;s headless Chromium starts in milliseconds and runs the suite fast enough to use as the default. Backends are stubbed at the network layer with &lt;code&gt;page.route&lt;/code&gt; so the same fixtures drive component tests today and end-to-end smoke tests later.&lt;/p&gt;</description></item><item><title>Event Consumer</title><link>https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/event-consumer/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/event-consumer/</guid><description>&lt;p&gt;A consumer of messages from Kafka, SQS, RabbitMQ, Pub/Sub, or similar. Reads messages, processes them, often updates state and produces downstream messages. The &amp;ldquo;public interface&amp;rdquo; is the topic or queue and the schema of messages on it.&lt;/p&gt;
&lt;p&gt;This pattern has problems the &lt;a href="https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/api-provider/"&gt;API provider&lt;/a&gt; and &lt;a href="https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/api-consumer/"&gt;API consumer&lt;/a&gt; patterns don&amp;rsquo;t have: ordering, replay, poison messages, dead-letter queues, and delivery semantics (at-most-once, at-least-once, exactly-once-with-effort).&lt;/p&gt;
&lt;h2 id="what-needs-covered"&gt;What needs covered&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Layer&lt;/th&gt;
 &lt;th&gt;Concern&lt;/th&gt;
 &lt;th&gt;Test type&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Message handler&lt;/td&gt;
 &lt;td&gt;Pure transformation per message&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#solitary-unit-test"&gt;Solitary unit tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Idempotency&lt;/td&gt;
 &lt;td&gt;Same message twice produces the same effect&lt;/td&gt;
 &lt;td&gt;In-process &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test"&gt;component tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Poison message handling&lt;/td&gt;
 &lt;td&gt;Malformed message goes to DLQ, doesn&amp;rsquo;t crash the consumer&lt;/td&gt;
 &lt;td&gt;In-process &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test"&gt;component tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Ordering&lt;/td&gt;
 &lt;td&gt;Out-of-order messages produce documented outcomes&lt;/td&gt;
 &lt;td&gt;In-process &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test"&gt;component tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Backpressure&lt;/td&gt;
 &lt;td&gt;Consumer slows when downstream is slow&lt;/td&gt;
 &lt;td&gt;Resilience &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test"&gt;component tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Broker contract&lt;/td&gt;
 &lt;td&gt;Topic, schema, headers&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#contract-test"&gt;Contract tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Broker client&lt;/td&gt;
 &lt;td&gt;Real protocol behavior, offset commits, consumer group rebalancing&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#adapter-integration-test"&gt;Adapter integration tests&lt;/a&gt; against a real broker container&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;figure class="figure"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 632"
 role="img" aria-labelledby="ecc-title ecc-desc"
 font-family="system-ui, -apple-system, sans-serif" font-size="13"&gt;

 &lt;title id="ecc-title"&gt;Event Consumer: layers and the tests that cover each&lt;/title&gt;
 &lt;desc id="ecc-desc"&gt;
 Six architectural layers stacked top to bottom. The first five (message handler logic, idempotency and ordering, dead-letter and poison-message handling, backpressure, and broker client) are inside the component boundary. Below the dashed component boundary, the external broker and schema registry are drawn with a dashed border. Each band shows its name, a one-line description, and the test types that exercise it as small coloured pills. Solitary unit tests cover handler logic. Component tests cover idempotency, dead-letter handling, ordering, and backpressure with the broker doubled. Adapter integration tests pin the broker protocol against a real broker container. Broker contract tests pin the topic, schema, and headers. Out-of-band synthetic publish confirms doubles still match the real broker.
 &lt;/desc&gt;

 &lt;rect width="720" height="632" fill="#fafafa" rx="4"/&gt;

 &lt;!-- Title --&gt;
 &lt;text x="360" y="28" text-anchor="middle" font-size="16" font-weight="bold" fill="#1a2a3a"&gt;
 Event Consumer: Layers and the Tests That Cover Each
 &lt;/text&gt;

 &lt;!-- Inside boundary label --&gt;
 &lt;text x="40" y="56" font-size="10" font-weight="700" letter-spacing="0.08em" fill="#224968"&gt;
 INSIDE THE COMPONENT BOUNDARY
 &lt;/text&gt;

 &lt;!-- Layer 1: Message handler logic --&gt;
 &lt;rect x="30" y="68" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="68" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="92" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Message handler logic&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#solitary-unit-test" target="_top"&gt;&lt;rect x="490" y="78" width="104" height="20" rx="10" fill="#4a6741"/&gt;&lt;text x="542" y="92" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Solitary unit&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="600" y="78" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="640" y="92" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="116" font-size="11" font-style="italic" fill="#556"&gt;Pure transformation per message&lt;/text&gt;

 &lt;!-- Layer 2: Idempotency and ordering --&gt;
 &lt;rect x="30" y="136" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="136" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="160" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Idempotency and ordering&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="600" y="146" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="640" y="160" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="184" font-size="11" font-style="italic" fill="#556"&gt;Duplicate delivery absorbed; ordering policy enforced&lt;/text&gt;

 &lt;!-- Layer 3: DLQ / poison --&gt;
 &lt;rect x="30" y="204" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="204" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="228" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Dead-letter and poison-message handling&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="600" y="214" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="640" y="228" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="252" font-size="11" font-style="italic" fill="#556"&gt;Malformed message routed to DLQ with correlation ID; consumer survives&lt;/text&gt;

 &lt;!-- Layer 4: Backpressure --&gt;
 &lt;rect x="30" y="272" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="272" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="296" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Backpressure&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="600" y="282" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="640" y="296" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="320" font-size="11" font-style="italic" fill="#556"&gt;Consumer slows when downstream is slow; offsets uncommitted on failure&lt;/text&gt;

 &lt;!-- Layer 5: Broker client --&gt;
 &lt;rect x="30" y="340" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="340" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="364" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Broker client&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#adapter-integration-test" target="_top"&gt;&lt;rect x="426" y="350" width="144" height="20" rx="10" fill="#30648e"/&gt;&lt;text x="498" y="364" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Adapter integration&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#contract-test" target="_top"&gt;&lt;rect x="576" y="350" width="50" height="20" rx="10" fill="#6c757d"/&gt;&lt;text x="601" y="364" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Brk.&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="632" y="350" width="48" height="20" rx="10" fill="#224968"/&gt;&lt;text x="656" y="364" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Comp.&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="388" font-size="11" font-style="italic" fill="#556"&gt;Protocol, offset commits, consumer-group rebalancing&lt;/text&gt;

 &lt;!-- Boundary divider --&gt;
 &lt;line x1="40" y1="418" x2="680" y2="418" stroke="#94a3b8" stroke-width="1" stroke-dasharray="6,4"/&gt;
 &lt;text x="360" y="414" text-anchor="middle" font-size="10" fill="#556" font-weight="600" font-style="italic"&gt;component boundary&lt;/text&gt;

 &lt;!-- Outside boundary label --&gt;
 &lt;text x="40" y="436" font-size="10" font-weight="700" letter-spacing="0.08em" fill="#6c757d"&gt;
 OUTSIDE THE BOUNDARY
 &lt;/text&gt;

 &lt;!-- Layer 6: External broker and schema registry --&gt;
 &lt;rect x="30" y="448" width="660" height="80" rx="6" fill="#edf2f7" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="6,4"/&gt;
 &lt;rect x="30" y="448" width="6" height="80" fill="#6c757d"/&gt;
 &lt;text x="50" y="472" font-size="14" font-weight="700" fill="#1a2a3a"&gt;External broker and schema registry&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="316" y="482" width="80" height="20" rx="10" fill="none" stroke="#224968" stroke-width="2"/&gt;&lt;text x="356" y="496" text-anchor="middle" font-size="11" font-weight="600" fill="#224968"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#adapter-integration-test" target="_top"&gt;&lt;rect x="402" y="482" width="120" height="20" rx="10" fill="#30648e"/&gt;&lt;text x="462" y="496" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Adapter integ.&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#contract-test" target="_top"&gt;&lt;rect x="528" y="482" width="98" height="20" rx="10" fill="#6c757d"/&gt;&lt;text x="577" y="496" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Broker contract&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#out-of-band-test" target="_top"&gt;&lt;rect x="632" y="482" width="48" height="20" rx="10" fill="#6c757d"/&gt;&lt;text x="656" y="496" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;OOB&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="514" font-size="11" font-style="italic" fill="#556"&gt;Doubled in component; real in adapter integration; OOB synthetic publish on a schedule.&lt;/text&gt;

 &lt;!-- Legend --&gt;
 &lt;rect x="40" y="564" width="30" height="16" rx="3" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="40" y="564" width="4" height="16" fill="#224968"/&gt;
 &lt;text x="78" y="576" font-size="11" fill="#556"&gt;internal layer&lt;/text&gt;
 &lt;rect x="320" y="565" width="24" height="14" rx="7" fill="#224968"/&gt;
 &lt;text x="354" y="576" font-size="11" fill="#556"&gt;real code under test&lt;/text&gt;

 &lt;rect x="40" y="588" width="30" height="16" rx="3" fill="#edf2f7" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4,3"/&gt;
 &lt;rect x="40" y="588" width="4" height="16" fill="#6c757d"/&gt;
 &lt;text x="78" y="600" font-size="11" fill="#556"&gt;external (dashed border)&lt;/text&gt;
 &lt;rect x="320" y="589" width="24" height="14" rx="7" fill="none" stroke="#224968" stroke-width="2"/&gt;
 &lt;text x="354" y="600" font-size="11" fill="#556"&gt;doubled in this test&lt;/text&gt;

&lt;/svg&gt;
&lt;figcaption class="figure-caption sr-only"&gt;Layered diagram of an event consumer with six architectural layers. The first five (message handler logic, idempotency and ordering, dead-letter and poison-message handling, backpressure, broker client) are inside the component boundary. Below the dashed boundary, the external broker and schema registry are drawn with a dashed border. Solitary unit tests cover handler logic. Component tests cover idempotency, dead-letter handling, ordering, and backpressure with the broker doubled. Adapter integration tests pin the broker protocol against a real broker container. Broker contract tests pin the topic, schema, and headers. Out-of-band synthetic publish confirms the doubles still match the real broker.&lt;/figcaption&gt;&lt;/figure&gt;

&lt;h2 id="positive-test-cases"&gt;Positive test cases&lt;/h2&gt;
&lt;p&gt;Common cases to consider, not an exhaustive list. Drop items that don&amp;rsquo;t apply and add ones the pattern doesn&amp;rsquo;t mention but your component needs.&lt;/p&gt;</description></item><item><title>Event Producer</title><link>https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/event-producer/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/event-producer/</guid><description>&lt;p&gt;The producer side, often paired with the &lt;a href="https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/event-consumer/"&gt;Event consumer&lt;/a&gt; pattern in the same service. After a state change, the service publishes a message that downstream consumers depend on.&lt;/p&gt;
&lt;p&gt;The hard problems differ from the consumer side: atomicity with persistence (did the DB row commit &lt;em&gt;and&lt;/em&gt; the message publish?), exactly-once semantics that require an outbox or two-phase commit, and downstream consumer dependence on schema, routing key, and headers.&lt;/p&gt;
&lt;h2 id="what-needs-covered"&gt;What needs covered&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Layer&lt;/th&gt;
 &lt;th&gt;Concern&lt;/th&gt;
 &lt;th&gt;Test type&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Outbox / transactional emit&lt;/td&gt;
 &lt;td&gt;DB write and message emit happen as a unit&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test"&gt;Component tests&lt;/a&gt; with real DB + broker double&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Produced message contract&lt;/td&gt;
 &lt;td&gt;Schema, headers, routing&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#contract-test"&gt;Provider-side contract tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Routing&lt;/td&gt;
 &lt;td&gt;Right topic and key per event type&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test"&gt;Component tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Retry on broker unavailable&lt;/td&gt;
 &lt;td&gt;Outbox drains once broker recovers&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test"&gt;Component tests&lt;/a&gt; with fault-injecting broker client double&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Trace propagation&lt;/td&gt;
 &lt;td&gt;Trace context in headers matches the inbound request&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test"&gt;Component tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;figure class="figure"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 572"
 role="img" aria-labelledby="epc-title epc-desc"
 font-family="system-ui, -apple-system, sans-serif" font-size="13"&gt;

 &lt;title id="epc-title"&gt;Event Producer: layers and the tests that cover each&lt;/title&gt;
 &lt;desc id="epc-desc"&gt;
 Five architectural layers stacked top to bottom. The first three (domain emit decision, outbox or transactional emit, and broker client) are inside the component boundary. Below the dashed component boundary, the external broker and the database used by the outbox are drawn with dashed borders. Each band shows its name, a one-line description, and the test types that exercise it as small coloured pills. Solitary unit tests cover the emit decision logic. Component tests cover outbox atomicity, retry on broker unavailable, and trace propagation, run with a real database and a doubled broker. Adapter integration pins the broker protocol against a real broker container. Provider contract verification runs against every consumer's published expectations. Out-of-band synthetic state change confirms the message arrives in the real broker.
 &lt;/desc&gt;

 &lt;rect width="720" height="572" fill="#fafafa" rx="4"/&gt;

 &lt;!-- Title --&gt;
 &lt;text x="360" y="28" text-anchor="middle" font-size="16" font-weight="bold" fill="#1a2a3a"&gt;
 Event Producer: Layers and the Tests That Cover Each
 &lt;/text&gt;

 &lt;!-- Inside boundary label --&gt;
 &lt;text x="40" y="56" font-size="10" font-weight="700" letter-spacing="0.08em" fill="#224968"&gt;
 INSIDE THE COMPONENT BOUNDARY
 &lt;/text&gt;

 &lt;!-- Layer 1: Domain emit decision --&gt;
 &lt;rect x="30" y="68" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="68" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="92" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Domain emit decision&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#solitary-unit-test" target="_top"&gt;&lt;rect x="490" y="78" width="104" height="20" rx="10" fill="#4a6741"/&gt;&lt;text x="542" y="92" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Solitary unit&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="600" y="78" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="640" y="92" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="116" font-size="11" font-style="italic" fill="#556"&gt;When, what, and which routing key&lt;/text&gt;

 &lt;!-- Layer 2: Outbox / transactional emit --&gt;
 &lt;rect x="30" y="136" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="136" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="160" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Outbox or transactional emit&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="600" y="146" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="640" y="160" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="184" font-size="11" font-style="italic" fill="#556"&gt;DB write and message emit happen as a unit; trace propagation&lt;/text&gt;

 &lt;!-- Layer 3: Broker client --&gt;
 &lt;rect x="30" y="204" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="204" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="228" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Broker client&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#adapter-integration-test" target="_top"&gt;&lt;rect x="426" y="214" width="144" height="20" rx="10" fill="#30648e"/&gt;&lt;text x="498" y="228" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Adapter integration&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#contract-test" target="_top"&gt;&lt;rect x="576" y="214" width="50" height="20" rx="10" fill="#6c757d"/&gt;&lt;text x="601" y="228" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Prv.&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="632" y="214" width="48" height="20" rx="10" fill="#224968"/&gt;&lt;text x="656" y="228" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Comp.&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="252" font-size="11" font-style="italic" fill="#556"&gt;Protocol, headers, retry on broker unavailable&lt;/text&gt;

 &lt;!-- Boundary divider --&gt;
 &lt;line x1="40" y1="286" x2="680" y2="286" stroke="#94a3b8" stroke-width="1" stroke-dasharray="6,4"/&gt;
 &lt;text x="360" y="282" text-anchor="middle" font-size="10" fill="#556" font-weight="600" font-style="italic"&gt;component boundary&lt;/text&gt;

 &lt;!-- Outside boundary label --&gt;
 &lt;text x="40" y="304" font-size="10" font-weight="700" letter-spacing="0.08em" fill="#6c757d"&gt;
 OUTSIDE THE BOUNDARY
 &lt;/text&gt;

 &lt;!-- Layer 4: External broker --&gt;
 &lt;rect x="30" y="316" width="660" height="80" rx="6" fill="#edf2f7" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="6,4"/&gt;
 &lt;rect x="30" y="316" width="6" height="80" fill="#6c757d"/&gt;
 &lt;text x="50" y="340" font-size="14" font-weight="700" fill="#1a2a3a"&gt;External broker&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="316" y="350" width="80" height="20" rx="10" fill="none" stroke="#224968" stroke-width="2"/&gt;&lt;text x="356" y="364" text-anchor="middle" font-size="11" font-weight="600" fill="#224968"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#adapter-integration-test" target="_top"&gt;&lt;rect x="402" y="350" width="120" height="20" rx="10" fill="#30648e"/&gt;&lt;text x="462" y="364" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Adapter integ.&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#contract-test" target="_top"&gt;&lt;rect x="528" y="350" width="98" height="20" rx="10" fill="#6c757d"/&gt;&lt;text x="577" y="364" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Provider contract&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#out-of-band-test" target="_top"&gt;&lt;rect x="632" y="350" width="48" height="20" rx="10" fill="#6c757d"/&gt;&lt;text x="656" y="364" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;OOB&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="382" font-size="11" font-style="italic" fill="#556"&gt;Doubled in component; real in adapter integration; OOB synthetic state change on a schedule.&lt;/text&gt;

 &lt;!-- Layer 5: Database (external) --&gt;
 &lt;rect x="30" y="404" width="660" height="60" rx="6" fill="#edf2f7" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="6,4"/&gt;
 &lt;rect x="30" y="404" width="6" height="60" fill="#6c757d"/&gt;
 &lt;text x="50" y="428" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Database (external)&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="450" y="414" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="490" y="428" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#adapter-integration-test" target="_top"&gt;&lt;rect x="536" y="414" width="144" height="20" rx="10" fill="#30648e"/&gt;&lt;text x="608" y="428" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Adapter integration&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="452" font-size="11" font-style="italic" fill="#556"&gt;Real DB in component to validate outbox atomicity&lt;/text&gt;

 &lt;!-- Legend --&gt;
 &lt;rect x="40" y="504" width="30" height="16" rx="3" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="40" y="504" width="4" height="16" fill="#224968"/&gt;
 &lt;text x="78" y="516" font-size="11" fill="#556"&gt;internal layer&lt;/text&gt;
 &lt;rect x="320" y="505" width="24" height="14" rx="7" fill="#224968"/&gt;
 &lt;text x="354" y="516" font-size="11" fill="#556"&gt;real code under test&lt;/text&gt;

 &lt;rect x="40" y="528" width="30" height="16" rx="3" fill="#edf2f7" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4,3"/&gt;
 &lt;rect x="40" y="528" width="4" height="16" fill="#6c757d"/&gt;
 &lt;text x="78" y="540" font-size="11" fill="#556"&gt;external (dashed border)&lt;/text&gt;
 &lt;rect x="320" y="529" width="24" height="14" rx="7" fill="none" stroke="#224968" stroke-width="2"/&gt;
 &lt;text x="354" y="540" font-size="11" fill="#556"&gt;doubled in this test&lt;/text&gt;

&lt;/svg&gt;
&lt;figcaption class="figure-caption sr-only"&gt;Layered diagram of an event producer with five architectural layers. The first three (domain emit decision, outbox or transactional emit, broker client) are inside the component boundary. Below the dashed boundary, the external broker and the database used by the outbox are drawn with dashed borders. Solitary unit tests cover the emit decision logic. Component tests cover outbox atomicity, retry on broker unavailable, and trace propagation, run with a real database and a doubled broker. Adapter integration pins the broker protocol against a real broker container. Provider contract verification runs against every consumer&amp;#39;s published expectations. Out-of-band synthetic state change confirms the message arrives in the real broker.&lt;/figcaption&gt;&lt;/figure&gt;

&lt;h2 id="positive-test-cases"&gt;Positive test cases&lt;/h2&gt;
&lt;p&gt;Common cases to consider, not an exhaustive list. Drop items that don&amp;rsquo;t apply and add ones the pattern doesn&amp;rsquo;t mention but your component needs.&lt;/p&gt;</description></item><item><title>CLI Tool or Library</title><link>https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/cli-library/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/cli-library/</guid><description>&lt;p&gt;A binary (CLI) or package (library) consumed by other developers. The &amp;ldquo;public interface&amp;rdquo; is the CLI invocation surface (argv, stdin, stdout, stderr, exit code) or the library&amp;rsquo;s exported API.&lt;/p&gt;
&lt;p&gt;The pattern is different because the consumer is a developer or another program, not a user clicking a button. Cross-platform behavior, semantic versioning, and backward compatibility matter more than they do for a service.&lt;/p&gt;
&lt;h2 id="what-needs-covered"&gt;What needs covered&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Layer&lt;/th&gt;
 &lt;th&gt;Concern&lt;/th&gt;
 &lt;th&gt;Test type&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Pure logic&lt;/td&gt;
 &lt;td&gt;Functions, classes, parsers&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#solitary-unit-test"&gt;Solitary unit tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;CLI invocation&lt;/td&gt;
 &lt;td&gt;Argument parsing, exit codes, output streams&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test"&gt;Component tests&lt;/a&gt; through the CLI entrypoint&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Cross-platform&lt;/td&gt;
 &lt;td&gt;Path separators, line endings, signal handling&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#cross-os-test-matrix"&gt;Cross-OS test matrix&lt;/a&gt; running the suite on every supported OS in &lt;a href="https://beyond.minimumcd.org/docs/reference/glossary/#ci-continuous-integration"&gt;CI&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Public API surface&lt;/td&gt;
 &lt;td&gt;Library&amp;rsquo;s exported types and functions&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#api-surface-test"&gt;API surface tests&lt;/a&gt; (snapshot of the public API; diff fails the build)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Documented examples&lt;/td&gt;
 &lt;td&gt;The README examples actually work&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#doctest"&gt;Doctests&lt;/a&gt; / executable docs&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;figure class="figure"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 612"
 role="img" aria-labelledby="clc-title clc-desc"
 font-family="system-ui, -apple-system, sans-serif" font-size="13"&gt;

 &lt;title id="clc-title"&gt;CLI Tool or Library: layers and the tests that cover each&lt;/title&gt;
 &lt;desc id="clc-desc"&gt;
 Five architectural layers stacked top to bottom. The first four (pure logic and parsing, CLI invocation surface or library API, file system and subprocess adapter, and documented README examples) are inside the component boundary. Below the dashed component boundary, the real OS, file system, and subprocess are drawn with a dashed border. Each band shows its name, a one-line description, and the test types that exercise it as small coloured pills. Solitary unit tests cover pure logic and parsing. Component tests cover invocation through the entrypoint. Adapter integration tests cover the file system and subprocess against the real OS in a temp directory. The API surface diff catches removal or rename of any public symbol. Doctests verify README examples run against the real binary or library. The cross-OS CI matrix runs the suite on every supported OS to catch platform-specific bugs.
 &lt;/desc&gt;

 &lt;rect width="720" height="612" fill="#fafafa" rx="4"/&gt;

 &lt;!-- Title --&gt;
 &lt;text x="360" y="28" text-anchor="middle" font-size="16" font-weight="bold" fill="#1a2a3a"&gt;
 CLI Tool or Library: Layers and the Tests That Cover Each
 &lt;/text&gt;

 &lt;!-- Inside boundary label --&gt;
 &lt;text x="40" y="56" font-size="10" font-weight="700" letter-spacing="0.08em" fill="#224968"&gt;
 INSIDE THE COMPONENT BOUNDARY
 &lt;/text&gt;

 &lt;!-- Layer 1: Pure logic / parsing --&gt;
 &lt;rect x="30" y="68" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="68" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="92" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Pure logic and parsing&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#solitary-unit-test" target="_top"&gt;&lt;rect x="490" y="78" width="104" height="20" rx="10" fill="#4a6741"/&gt;&lt;text x="542" y="92" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Solitary unit&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="600" y="78" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="640" y="92" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="116" font-size="11" font-style="italic" fill="#556"&gt;Functions, classes, parsers; no I/O&lt;/text&gt;

 &lt;!-- Layer 2: CLI invocation / library API --&gt;
 &lt;rect x="30" y="136" width="660" height="80" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="136" width="6" height="80" fill="#224968"/&gt;
 &lt;text x="50" y="160" font-size="14" font-weight="700" fill="#1a2a3a"&gt;CLI invocation surface or library API&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="316" y="170" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="356" y="184" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#api-surface-test" target="_top"&gt;&lt;rect x="402" y="170" width="106" height="20" rx="10" fill="#6c757d"/&gt;&lt;text x="455" y="184" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;API surface diff&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#doctest" target="_top"&gt;&lt;rect x="514" y="170" width="76" height="20" rx="10" fill="#6c757d"/&gt;&lt;text x="552" y="184" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Doctests&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#cross-os-test-matrix" target="_top"&gt;&lt;rect x="596" y="170" width="84" height="20" rx="10" fill="#224968"/&gt;&lt;text x="638" y="184" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Cross-OS CI&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="202" font-size="11" font-style="italic" fill="#556"&gt;argv, stdin, stdout, stderr, exit code, --help, exported symbols&lt;/text&gt;

 &lt;!-- Layer 3: FS / subprocess gateway --&gt;
 &lt;rect x="30" y="224" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="224" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="248" font-size="14" font-weight="700" fill="#1a2a3a"&gt;File system and subprocess adapter&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="386" y="234" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="426" y="248" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#adapter-integration-test" target="_top"&gt;&lt;rect x="472" y="234" width="120" height="20" rx="10" fill="#30648e"/&gt;&lt;text x="532" y="248" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Adapter integ.&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#cross-os-test-matrix" target="_top"&gt;&lt;rect x="598" y="234" width="82" height="20" rx="10" fill="#224968"/&gt;&lt;text x="639" y="248" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Cross-OS&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="272" font-size="11" font-style="italic" fill="#556"&gt;Paths, encodings, signal handling, spawn semantics&lt;/text&gt;

 &lt;!-- Layer 4: Documented README examples --&gt;
 &lt;rect x="30" y="292" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="292" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="316" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Documented README examples&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#doctest" target="_top"&gt;&lt;rect x="600" y="302" width="80" height="20" rx="10" fill="#6c757d"/&gt;&lt;text x="640" y="316" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Doctests&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="340" font-size="11" font-style="italic" fill="#556"&gt;Examples in the docs actually run against the real binary or library&lt;/text&gt;

 &lt;!-- Boundary divider --&gt;
 &lt;line x1="40" y1="374" x2="680" y2="374" stroke="#94a3b8" stroke-width="1" stroke-dasharray="6,4"/&gt;
 &lt;text x="360" y="370" text-anchor="middle" font-size="10" fill="#556" font-weight="600" font-style="italic"&gt;component boundary&lt;/text&gt;

 &lt;!-- Outside boundary label --&gt;
 &lt;text x="40" y="392" font-size="10" font-weight="700" letter-spacing="0.08em" fill="#6c757d"&gt;
 OUTSIDE THE BOUNDARY
 &lt;/text&gt;

 &lt;!-- Layer 5: Real OS / FS / subprocess --&gt;
 &lt;rect x="30" y="404" width="660" height="80" rx="6" fill="#edf2f7" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="6,4"/&gt;
 &lt;rect x="30" y="404" width="6" height="80" fill="#6c757d"/&gt;
 &lt;text x="50" y="428" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Real OS, file system, subprocess&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="386" y="438" width="80" height="20" rx="10" fill="none" stroke="#224968" stroke-width="2"/&gt;&lt;text x="426" y="452" text-anchor="middle" font-size="11" font-weight="600" fill="#224968"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#adapter-integration-test" target="_top"&gt;&lt;rect x="472" y="438" width="120" height="20" rx="10" fill="#30648e"/&gt;&lt;text x="532" y="452" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Adapter integ.&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#cross-os-test-matrix" target="_top"&gt;&lt;rect x="598" y="438" width="82" height="20" rx="10" fill="#224968"/&gt;&lt;text x="639" y="452" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Cross-OS&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="470" font-size="11" font-style="italic" fill="#556"&gt;Path separators, line endings, signals. Doubled in component; real in adapter integration and the cross-OS matrix.&lt;/text&gt;

 &lt;!-- Legend --&gt;
 &lt;rect x="40" y="538" width="30" height="16" rx="3" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="40" y="538" width="4" height="16" fill="#224968"/&gt;
 &lt;text x="78" y="550" font-size="11" fill="#556"&gt;internal layer&lt;/text&gt;
 &lt;rect x="320" y="539" width="24" height="14" rx="7" fill="#224968"/&gt;
 &lt;text x="354" y="550" font-size="11" fill="#556"&gt;real code under test&lt;/text&gt;

 &lt;rect x="40" y="562" width="30" height="16" rx="3" fill="#edf2f7" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4,3"/&gt;
 &lt;rect x="40" y="562" width="4" height="16" fill="#6c757d"/&gt;
 &lt;text x="78" y="574" font-size="11" fill="#556"&gt;external (dashed border)&lt;/text&gt;
 &lt;rect x="320" y="563" width="24" height="14" rx="7" fill="none" stroke="#224968" stroke-width="2"/&gt;
 &lt;text x="354" y="574" font-size="11" fill="#556"&gt;doubled in this test&lt;/text&gt;

&lt;/svg&gt;
&lt;figcaption class="figure-caption sr-only"&gt;Layered diagram of a CLI tool or library with five architectural layers. The first four (pure logic and parsing, CLI invocation surface or library API, file system and subprocess adapter, documented README examples) are inside the component boundary. Below the dashed boundary, the real OS, file system, and subprocess are drawn with a dashed border. Solitary unit tests cover pure logic and parsing. Component tests cover invocation through the entrypoint. Adapter integration tests cover the file system and subprocess against the real OS in a temp directory. The API surface diff catches removal or rename of any public symbol. Doctests verify README examples run against the real binary or library. The cross-OS CI matrix runs the suite on every supported OS to catch platform-specific bugs.&lt;/figcaption&gt;&lt;/figure&gt;

&lt;h2 id="positive-test-cases"&gt;Positive test cases&lt;/h2&gt;
&lt;p&gt;Common cases to consider, not an exhaustive list. Drop items that don&amp;rsquo;t apply and add ones the pattern doesn&amp;rsquo;t mention but your component needs.&lt;/p&gt;</description></item><item><title>Stateful Service</title><link>https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/stateful-service/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://beyond.minimumcd.org/docs/testing/applied-testing-strategies/patterns/stateful-service/</guid><description>&lt;p&gt;A service that maintains long-lived in-memory state: caches, in-memory aggregates, leader-elected coordinators, websocket gateways, real-time engines, sticky-session servers.&lt;/p&gt;
&lt;p&gt;The hard problems are concurrency, recovery, and unbounded growth. Stateful services fail in ways stateless services do not.&lt;/p&gt;
&lt;h2 id="what-needs-covered"&gt;What needs covered&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Layer&lt;/th&gt;
 &lt;th&gt;Concern&lt;/th&gt;
 &lt;th&gt;Test type&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;State machine logic&lt;/td&gt;
 &lt;td&gt;Pure transitions&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#solitary-unit-test"&gt;Solitary unit tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Persistence and checkpointing&lt;/td&gt;
 &lt;td&gt;State survives restart or rebuilds correctly&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test"&gt;Component tests&lt;/a&gt; with real persistence&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Recovery from crash&lt;/td&gt;
 &lt;td&gt;Restart converges to a consistent state&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test"&gt;Component tests&lt;/a&gt; that simulate crash mid-write&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Leader election&lt;/td&gt;
 &lt;td&gt;Only one leader; transitions are observable; split-brain is impossible&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#cluster-test"&gt;Cluster tests&lt;/a&gt; with real consensus library&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Replication&lt;/td&gt;
 &lt;td&gt;Followers stay in sync; backpressure is documented&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#cluster-test"&gt;Cluster tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Memory bounds&lt;/td&gt;
 &lt;td&gt;State doesn&amp;rsquo;t grow unbounded; eviction policy holds&lt;/td&gt;
 &lt;td&gt;Long-running &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#soak-test"&gt;soak tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Connection lifecycle&lt;/td&gt;
 &lt;td&gt;Sessions clean up on disconnect; reconnect is documented&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test"&gt;Component tests&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;figure class="figure"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 632"
 role="img" aria-labelledby="ssc-title ssc-desc"
 font-family="system-ui, -apple-system, sans-serif" font-size="13"&gt;

 &lt;title id="ssc-title"&gt;Stateful Service: layers and the tests that cover each&lt;/title&gt;
 &lt;desc id="ssc-desc"&gt;
 Six architectural layers stacked top to bottom. The first five (state machine logic, persistence and recovery, single-node concurrency, replication and leader election, and memory bounds and long-run behaviour) are inside the component boundary. Below the dashed component boundary, the persistence engine is drawn with a dashed border. Each band shows its name, a one-line description, and the test types that exercise it as small coloured pills. Solitary unit tests cover state transitions. Component tests cover persistence, recovery, and single-node concurrency. Cluster tests exercise replication and leader election against a multi-node testcontainer setup. Soak and chaos tests run out of band against a deployed instance.
 &lt;/desc&gt;

 &lt;rect width="720" height="632" fill="#fafafa" rx="4"/&gt;

 &lt;!-- Title --&gt;
 &lt;text x="360" y="28" text-anchor="middle" font-size="16" font-weight="bold" fill="#1a2a3a"&gt;
 Stateful Service: Layers and the Tests That Cover Each
 &lt;/text&gt;

 &lt;!-- Inside boundary label --&gt;
 &lt;text x="40" y="56" font-size="10" font-weight="700" letter-spacing="0.08em" fill="#224968"&gt;
 INSIDE THE COMPONENT BOUNDARY
 &lt;/text&gt;

 &lt;!-- Layer 1: State machine logic --&gt;
 &lt;rect x="30" y="68" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="68" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="92" font-size="14" font-weight="700" fill="#1a2a3a"&gt;State machine logic&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#solitary-unit-test" target="_top"&gt;&lt;rect x="490" y="78" width="104" height="20" rx="10" fill="#4a6741"/&gt;&lt;text x="542" y="92" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Solitary unit&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="600" y="78" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="640" y="92" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="116" font-size="11" font-style="italic" fill="#556"&gt;Pure transitions; documented machine&lt;/text&gt;

 &lt;!-- Layer 2: Persistence and recovery --&gt;
 &lt;rect x="30" y="136" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="136" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="160" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Persistence and recovery&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="600" y="146" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="640" y="160" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="184" font-size="11" font-style="italic" fill="#556"&gt;State survives restart; consistent state after crash mid-write&lt;/text&gt;

 &lt;!-- Layer 3: Single-node concurrency --&gt;
 &lt;rect x="30" y="204" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="204" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="228" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Single-node concurrency&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="600" y="214" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="640" y="228" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="252" font-size="11" font-style="italic" fill="#556"&gt;Serialized mutations; connection lifecycle and reconnect&lt;/text&gt;

 &lt;!-- Layer 4: Replication and leader election --&gt;
 &lt;rect x="30" y="272" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="272" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="296" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Replication and leader election&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#cluster-test" target="_top"&gt;&lt;rect x="600" y="282" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="640" y="296" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Cluster tests&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="320" font-size="11" font-style="italic" fill="#556"&gt;Followers stay in sync; minority partition steps down; no split-brain&lt;/text&gt;

 &lt;!-- Layer 5: Memory bounds / long-run --&gt;
 &lt;rect x="30" y="340" width="660" height="60" rx="6" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="30" y="340" width="6" height="60" fill="#224968"/&gt;
 &lt;text x="50" y="364" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Memory bounds and long-run behaviour&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#soak-test" target="_top"&gt;&lt;rect x="566" y="350" width="114" height="20" rx="10" fill="#6c757d"/&gt;&lt;text x="623" y="364" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Soak / chaos (OOB)&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="388" font-size="11" font-style="italic" fill="#556"&gt;Eviction policy holds; no unbounded growth; replication-lag stays in budget&lt;/text&gt;

 &lt;!-- Boundary divider --&gt;
 &lt;line x1="40" y1="418" x2="680" y2="418" stroke="#94a3b8" stroke-width="1" stroke-dasharray="6,4"/&gt;
 &lt;text x="360" y="414" text-anchor="middle" font-size="10" fill="#556" font-weight="600" font-style="italic"&gt;component boundary&lt;/text&gt;

 &lt;!-- Outside boundary label --&gt;
 &lt;text x="40" y="436" font-size="10" font-weight="700" letter-spacing="0.08em" fill="#6c757d"&gt;
 OUTSIDE THE BOUNDARY
 &lt;/text&gt;

 &lt;!-- Layer 6: Persistence engine (external) --&gt;
 &lt;rect x="30" y="448" width="660" height="80" rx="6" fill="#edf2f7" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="6,4"/&gt;
 &lt;rect x="30" y="448" width="6" height="80" fill="#6c757d"/&gt;
 &lt;text x="50" y="472" font-size="14" font-weight="700" fill="#1a2a3a"&gt;Persistence engine (external)&lt;/text&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#component-test" target="_top"&gt;&lt;rect x="316" y="482" width="80" height="20" rx="10" fill="none" stroke="#224968" stroke-width="2"/&gt;&lt;text x="356" y="496" text-anchor="middle" font-size="11" font-weight="600" fill="#224968"&gt;Component&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#adapter-integration-test" target="_top"&gt;&lt;rect x="402" y="482" width="120" height="20" rx="10" fill="#30648e"/&gt;&lt;text x="462" y="496" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Adapter integ.&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#cluster-test" target="_top"&gt;&lt;rect x="528" y="482" width="80" height="20" rx="10" fill="#224968"/&gt;&lt;text x="568" y="496" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Cluster&lt;/text&gt;&lt;/a&gt;
 &lt;a href="https://beyond.minimumcd.org/docs/testing/glossary/#soak-test" target="_top"&gt;&lt;rect x="614" y="482" width="66" height="20" rx="10" fill="#6c757d"/&gt;&lt;text x="647" y="496" text-anchor="middle" font-size="11" font-weight="600" fill="#fff"&gt;Soak&lt;/text&gt;&lt;/a&gt;
 &lt;text x="50" y="514" font-size="11" font-style="italic" fill="#556"&gt;Production engine. Doubled or in-memory in single-node component; real in gateway and cluster.&lt;/text&gt;

 &lt;!-- Legend --&gt;
 &lt;rect x="40" y="564" width="30" height="16" rx="3" fill="#ffffff" stroke="#d0d7de" stroke-width="1"/&gt;
 &lt;rect x="40" y="564" width="4" height="16" fill="#224968"/&gt;
 &lt;text x="78" y="576" font-size="11" fill="#556"&gt;internal layer&lt;/text&gt;
 &lt;rect x="320" y="565" width="24" height="14" rx="7" fill="#224968"/&gt;
 &lt;text x="354" y="576" font-size="11" fill="#556"&gt;real code under test&lt;/text&gt;

 &lt;rect x="40" y="588" width="30" height="16" rx="3" fill="#edf2f7" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4,3"/&gt;
 &lt;rect x="40" y="588" width="4" height="16" fill="#6c757d"/&gt;
 &lt;text x="78" y="600" font-size="11" fill="#556"&gt;external (dashed border)&lt;/text&gt;
 &lt;rect x="320" y="589" width="24" height="14" rx="7" fill="none" stroke="#224968" stroke-width="2"/&gt;
 &lt;text x="354" y="600" font-size="11" fill="#556"&gt;doubled in this test&lt;/text&gt;

&lt;/svg&gt;
&lt;figcaption class="figure-caption sr-only"&gt;Layered diagram of a stateful service with six architectural layers. The first five (state machine logic, persistence and recovery, single-node concurrency, replication and leader election, memory bounds and long-run behavior) are inside the component boundary. Below the dashed boundary, the persistence engine is drawn with a dashed border. Solitary unit tests cover state transitions. Component tests cover persistence, recovery, and single-node concurrency. Cluster tests exercise replication and leader election against a multi-node testcontainer setup. Out-of-band soak and chaos tests catch unbounded growth, slow leaks, and replication-lag drift against a deployed instance.&lt;/figcaption&gt;&lt;/figure&gt;

&lt;h2 id="positive-test-cases"&gt;Positive test cases&lt;/h2&gt;
&lt;p&gt;Common cases to consider, not an exhaustive list. Drop items that don&amp;rsquo;t apply and add ones the pattern doesn&amp;rsquo;t mention but your component needs.&lt;/p&gt;</description></item></channel></rss>