<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>api Archives - CodeCraft Diary</title>
	<atom:link href="https://codecraftdiary.com/tag/api/feed/" rel="self" type="application/rss+xml" />
	<link>https://codecraftdiary.com/tag/api/</link>
	<description></description>
	<lastBuildDate>Sat, 14 Feb 2026 12:48:07 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0</generator>

<image>
	<url>https://codecraftdiary.com/wp-content/uploads/2025/10/cropped-IMG_3463-32x32.png</url>
	<title>api Archives - CodeCraft Diary</title>
	<link>https://codecraftdiary.com/tag/api/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Contract Testing External APIs in PHP with Pact (Real Laravel Example)</title>
		<link>https://codecraftdiary.com/2026/02/14/contract-testing-external-apis-in-php-with-pact-real-laravel-example/</link>
					<comments>https://codecraftdiary.com/2026/02/14/contract-testing-external-apis-in-php-with-pact-real-laravel-example/#respond</comments>
		
		<dc:creator><![CDATA[codecraftdiary]]></dc:creator>
		<pubDate>Sat, 14 Feb 2026 12:43:26 +0000</pubDate>
				<category><![CDATA[Testing]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[mocking]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[software-design]]></category>
		<category><![CDATA[testing]]></category>
		<guid isPermaLink="false">https://codecraftdiary.com/?p=3173</guid>

					<description><![CDATA[<p>Testing integrations with external APIs is one of the most fragile parts of any web application. In theory, we write tests, mock HTTP clients, and feel confident. In practice, APIs change, fields disappear, status codes shift, and production breaks anyway. I learned this the hard way. In one project, all my tests were green. My [&#8230;]</p>
<p>The post <a href="https://codecraftdiary.com/2026/02/14/contract-testing-external-apis-in-php-with-pact-real-laravel-example/">Contract Testing External APIs in PHP with Pact (Real Laravel Example)</a> appeared first on <a href="https://codecraftdiary.com">CodeCraft Diary</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Testing integrations with external APIs is one of the most fragile parts of any web application. In theory, we write tests, mock HTTP clients, and feel confident. In practice, APIs change, fields disappear, status codes shift, and production breaks anyway.</p>



<p class="wp-block-paragraph">I learned this the hard way.</p>



<p class="wp-block-paragraph">In one project, all my tests were green. My mocks returned exactly what I expected. A week later, the external API removed one field from the response. My mocks didn’t know about the change. Production did.</p>



<p class="wp-block-paragraph">Mocks told me everything was fine. Reality disagreed.</p>



<p class="wp-block-paragraph">This is exactly the kind of problem <strong>contract testing</strong> is meant to solve. In this article, I’ll show you how to use contract testing for external APIs in PHP with <strong>Pact</strong>, using a real-world <strong>Laravel</strong> example.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading has-text-color has-link-color wp-elements-61b2144564a5c21db7dba69781163bea" style="color:#0092eb">Why Mocking External APIs Is Not Enough</h2>



<p class="wp-block-paragraph">Mocking external APIs is useful. I still do it. It makes tests fast, deterministic, and cheap. But mocks have one fatal flaw:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">They only test your assumptions about the API, not the API itself.</p>
</blockquote>



<p class="wp-block-paragraph">Typical problems I’ve seen in production:</p>



<ul class="wp-block-list">
<li>The API removes or renames a field.</li>



<li>A nested structure changes shape.</li>



<li>The API starts returning <code>404</code> instead of <code>200</code> in some edge cases.</li>



<li>The API adds a required field to the request body.</li>
</ul>



<p class="wp-block-paragraph">Your mocks will never catch this unless you manually update them. That means mocks alone can silently drift away from reality.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading has-text-color has-link-color wp-elements-38cc5459dc519e6c7735663aba2991d1" style="color:#0092eb">What Is Contract Testing (In Plain English)</h2>



<p class="wp-block-paragraph">Contract testing sits between mocking and full integration tests.</p>



<p class="wp-block-paragraph">Instead of saying:</p>



<blockquote class="wp-block-quote is-style-plain is-layout-flow wp-block-quote-is-layout-flow">
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">“This is what I think the API returns,”</p>
</blockquote>
</blockquote>



<p class="wp-block-paragraph">you say:</p>



<blockquote class="wp-block-quote is-style-plain is-layout-flow wp-block-quote-is-layout-flow">
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">“This is the contract between my app (consumer) and the API (provider).”</p>
</blockquote>
</blockquote>



<p class="wp-block-paragraph">With consumer-driven contract testing:</p>



<ul class="wp-block-list">
<li>The <strong>consumer</strong> defines what it expects from the API.</li>



<li>The <strong>provider</strong> must verify that it actually fulfills this contract.</li>
</ul>



<p class="wp-block-paragraph">If the provider changes something that breaks the contract, tests fail <strong>before production breaks</strong>.</p>



<p class="wp-block-paragraph">This turns breaking API changes from a runtime surprise into a build-time failure.</p>



<p class="wp-block-paragraph"><em>Diagram: How contract testing works between a Laravel consumer, Pact mock server, and the real API provider.</em></p>



<figure class="wp-block-image size-large is-resized"><img fetchpriority="high" decoding="async" width="1024" height="683" src="https://codecraftdiary.com/wp-content/uploads/2026/02/Contract-testing-flow-diagram-1024x683.webp" alt="" class="wp-image-3179" style="aspect-ratio:1.499297204435421;width:699px;height:auto" srcset="https://codecraftdiary.com/wp-content/uploads/2026/02/Contract-testing-flow-diagram-1024x683.webp 1024w, https://codecraftdiary.com/wp-content/uploads/2026/02/Contract-testing-flow-diagram-300x200.webp 300w, https://codecraftdiary.com/wp-content/uploads/2026/02/Contract-testing-flow-diagram-768x512.webp 768w, https://codecraftdiary.com/wp-content/uploads/2026/02/Contract-testing-flow-diagram.webp 1536w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading has-text-color has-link-color wp-elements-e55492e0a7790218d689e747fb632e40" style="color:#0092eb">Real-World Scenario: Laravel as API Consumer</h2>



<p class="wp-block-paragraph">Let’s assume a Laravel application integrates with an external billing API:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-Roboto-Mono.ttf" style="font-size:1rem;font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * 1rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>GET /api/customers/{id}

Response:
{
  "id": 123,
  "email": "john@example.com",
  "is_active": true
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">GET /api/customers/{</span><span style="color: #F44747">id</span><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">Response:</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">&quot;id&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #B5CEA8">123</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">&quot;email&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&quot;john@example.com&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">&quot;is_active&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #569CD6">true</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre><span style="display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-start;background-color:#1E1E1E;color:#c7c7c7;font-size:12px;line-height:1;position:relative">JSON</span></div>



<p class="wp-block-paragraph">Your Laravel service:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-Roboto-Mono.ttf" style="font-size:1rem;font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * 1rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>class BillingClient
{
    public function getCustomer(int $id): array
    {
        $response = Http::get("https://billing.example.com/api/customers/{$id}");

        return $response->json();
    }
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">BillingClient</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">getCustomer</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">int</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$id</span><span style="color: #D4D4D4">): </span><span style="color: #569CD6">array</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">$response</span><span style="color: #D4D4D4"> = </span><span style="color: #4EC9B0">Http</span><span style="color: #D4D4D4">::</span><span style="color: #DCDCAA">get</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;https://billing.example.com/api/customers/{</span><span style="color: #9CDCFE">$id</span><span style="color: #CE9178">}&quot;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$response</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">json</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre><span style="display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-start;background-color:#1E1E1E;color:#c7c7c7;font-size:12px;line-height:1;position:relative">PHP</span></div>



<p class="wp-block-paragraph">Your application relies on:</p>



<ul class="wp-block-list">
<li><code>email</code></li>



<li><code>is_active</code></li>
</ul>



<p class="wp-block-paragraph">If the provider removes <code>is_active</code>, your app breaks. Mocks won’t catch it. Contract testing will.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading has-text-color has-link-color wp-elements-628d444445d134f8756184e26e786e2c" style="color:#0092eb">Step-by-Step: Contract Testing with Pact in Laravel</h2>



<h3 class="wp-block-heading has-palette-color-4-color has-text-color has-link-color wp-elements-f6b58a27bb890bafae8ed296433819c7">Installing Pact for PHP</h3>



<p class="wp-block-paragraph">Install Pact dependencies for your test environment:</p>



<pre class="wp-block-code"><code>composer require --dev pact-foundation/pact-php</code></pre>



<p class="wp-block-paragraph">You’ll also need the Pact CLI running locally or in CI (usually via Docker).</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading has-text-color has-link-color wp-elements-88fbf33d542bf83ad10aa7446ff24ea6" style="color:#0092eb">Writing the Consumer Contract Test</h3>



<p class="wp-block-paragraph">The goal: define what your Laravel app <strong>expects</strong> from the API.</p>



<p class="wp-block-paragraph">Example consumer test:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-Roboto-Mono.ttf" style="font-size:1rem;font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * 1rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>public function test_it_fetches_customer_from_billing_api()
{
    $builder = new InteractionBuilder();
    
    $builder
        ->given('Customer 123 exists')
        ->uponReceiving('A request for customer 123')
        ->with(&#91;
            'method' => 'GET',
            'path'   => '/api/customers/123',
        &#93;)
        ->willRespondWith([
            'status' => 200,
            'headers' => &#91;'Content-Type' => 'application/json'&#93;,
            'body' => &#91;
                'id' => 123,
                'email' => 'john@example.com',
                'is_active' => true,
            &#93;,
        ]);

    $builder->verify(function () {
        $client = new BillingClient();
        $customer = $client->getCustomer(123);

        $this->assertSame('john@example.com', $customer&#91;'email'&#93;);
        $this->assertTrue($customer&#91;'is_active'&#93;);
    });
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">test_it_fetches_customer_from_billing_api</span><span style="color: #D4D4D4">()</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$builder</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">InteractionBuilder</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">    </span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$builder</span></span>
<span class="line"><span style="color: #D4D4D4">        -&gt;</span><span style="color: #DCDCAA">given</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39;Customer 123 exists&#39;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">        -&gt;</span><span style="color: #DCDCAA">uponReceiving</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39;A request for customer 123&#39;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">        -&gt;</span><span style="color: #DCDCAA">with</span><span style="color: #D4D4D4">(&#91;</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #CE9178">&#39;method&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #CE9178">&#39;GET&#39;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #CE9178">&#39;path&#39;</span><span style="color: #D4D4D4">   =&gt; </span><span style="color: #CE9178">&#39;/api/customers/123&#39;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">        &#93;)</span></span>
<span class="line"><span style="color: #D4D4D4">        -&gt;</span><span style="color: #DCDCAA">willRespondWith</span><span style="color: #D4D4D4">([</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #CE9178">&#39;status&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #B5CEA8">200</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #CE9178">&#39;headers&#39;</span><span style="color: #D4D4D4"> =&gt; &#91;</span><span style="color: #CE9178">&#39;Content-Type&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #CE9178">&#39;application/json&#39;</span><span style="color: #D4D4D4">&#93;,</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #CE9178">&#39;body&#39;</span><span style="color: #D4D4D4"> =&gt; &#91;</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #CE9178">&#39;id&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #B5CEA8">123</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #CE9178">&#39;email&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #CE9178">&#39;john@example.com&#39;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #CE9178">&#39;is_active&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #569CD6">true</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">            &#93;,</span></span>
<span class="line"><span style="color: #D4D4D4">        ]);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$builder</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">verify</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> () {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">$client</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">BillingClient</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">$customer</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">$client</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">getCustomer</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">123</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">assertSame</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39;john@example.com&#39;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">$customer</span><span style="color: #D4D4D4">&#91;</span><span style="color: #CE9178">&#39;email&#39;</span><span style="color: #D4D4D4">&#93;);</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">assertTrue</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$customer</span><span style="color: #D4D4D4">&#91;</span><span style="color: #CE9178">&#39;is_active&#39;</span><span style="color: #D4D4D4">&#93;);</span></span>
<span class="line"><span style="color: #D4D4D4">    });</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre><span style="display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-start;background-color:#1E1E1E;color:#c7c7c7;font-size:12px;line-height:1;position:relative">PHP</span></div>



<p class="wp-block-paragraph">This test:</p>



<ul class="wp-block-list">
<li>defines the expected request</li>



<li>defines the expected response</li>



<li>generates a <strong>contract file</strong> (Pact file)</li>
</ul>



<p class="wp-block-paragraph">This contract is the <strong>single source of truth</strong> between consumer and provider.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading has-text-color has-link-color wp-elements-9b5102f6aa14e3aed06d0e0cedc33a57" style="color:#0092eb">Verifying the Contract on the Provider Side</h3>



<p class="wp-block-paragraph">On the provider side, the API team runs Pact verification:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-Roboto-Mono.ttf" style="font-size:1rem;font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * 1rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>pact-verifier --provider-base-url=http://localhost:8000 \
 --pact-url=./pacts/billing-consumer.json
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #DCDCAA">pact-verifier</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--provider-base-url=http://localhost:8000</span><span style="color: #D4D4D4"> </span><span style="color: #D7BA7D">\</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--pact-url=./pacts/billing-consumer.json</span></span>
<span class="line"></span></code></pre><span style="display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-start;background-color:#1E1E1E;color:#c7c7c7;font-size:12px;line-height:1;position:relative">Bash</span></div>



<p class="wp-block-paragraph">If the API no longer returns <code>is_active</code>, verification fails.</p>



<p class="wp-block-paragraph">This is the key difference from mocks:</p>



<ul class="wp-block-list">
<li>mocks only test the consumer</li>



<li>contract testing tests the <strong>agreement</strong></li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading has-text-color has-link-color wp-elements-60f6d77b17f2509d3b9848e4ea0f43cd" style="color:#0092eb">Running Contract Tests in CI</h3>



<p class="wp-block-paragraph">This is where contract testing becomes powerful.</p>



<p class="wp-block-paragraph">Typical CI flow:</p>



<ol class="wp-block-list">
<li>Consumer tests generate Pact contracts.</li>



<li>Provider pipeline verifies contracts.</li>



<li>Pipeline fails if contracts are broken.</li>
</ol>



<p class="wp-block-paragraph">This creates a safety net:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">API changes cannot be deployed if they break existing consumers.</p>
</blockquote>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading has-text-color has-link-color wp-elements-08e8c4152a25791e845da3854e58f9c9" style="color:#0092eb">Common Mistakes with Contract Testing</h2>



<h3 class="wp-block-heading">Over-Specifying Responses</h3>



<p class="wp-block-paragraph">Don’t lock down every field if you don’t need it.<br>Only define what your app actually uses.</p>



<h3 class="wp-block-heading">Only Testing Happy Paths</h3>



<p class="wp-block-paragraph">Include error contracts:</p>



<ul class="wp-block-list">
<li>404 responses</li>



<li>validation errors</li>



<li>unauthorized responses</li>
</ul>



<h3 class="wp-block-heading">Not Versioning Contracts</h3>



<p class="wp-block-paragraph">Treat contracts like code. Version them.</p>



<h3 class="wp-block-heading">Not Enforcing Contracts in CI</h3>



<p class="wp-block-paragraph">If no pipeline enforces them, contract testing is just documentation.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading has-text-color has-link-color wp-elements-cf2e64a689b09ec738026175f62a3250" style="color:#0092eb">When Contract Testing Is Worth It (and When It’s Overkill)</h2>



<p class="wp-block-paragraph"><strong>Contract testing is worth it when:</strong></p>



<ul class="wp-block-list">
<li>multiple teams own different services</li>



<li>APIs evolve frequently</li>



<li>breaking changes are expensive</li>
</ul>



<p class="wp-block-paragraph"><strong>It’s probably overkill when:</strong></p>



<ul class="wp-block-list">
<li>the integration is trivial</li>



<li>you don’t control the provider</li>



<li>the API is stable and rarely changes</li>
</ul>



<p class="wp-block-paragraph">Contract testing is a scalpel, not a hammer. Use it where API stability matters.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading has-text-color has-link-color wp-elements-b77233e4ae2a1827c326fd2a90356849" style="color:#0092eb">How Contract Testing Fits into Your Laravel Testing Strategy</h2>



<p class="wp-block-paragraph">This is how I think about testing layers in real projects:</p>



<ul class="wp-block-list">
<li><strong>Unit tests</strong> – business logic</li>



<li><strong>Feature tests</strong> – HTTP flows</li>



<li><strong>Mocks</strong> – isolate slow or unstable services</li>



<li><strong>Contract tests</strong> – protect integrations from breaking changes</li>
</ul>



<p class="wp-block-paragraph">Mocks keep your tests fast.<br>Contract tests keep your integrations honest.</p>



<p class="wp-block-paragraph">They solve different problems and work best together.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading has-text-color has-link-color wp-elements-e6f4ac7d9acd8c30eb3fe27df30760b9" style="color:#0092eb">Final Thoughts</h2>



<p class="wp-block-paragraph">Mocking external APIs is necessary — but it’s not sufficient.</p>



<p class="wp-block-paragraph">If your application depends on external services and you’ve ever been surprised by a breaking API change in production, contract testing is the missing safety net.</p>



<p class="wp-block-paragraph">You don’t need to contract-test everything. Start with one critical integration. Add one contract. Let your CI enforce it. The first time a breaking change is caught before deployment, contract testing pays for itself.</p>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://codecraftdiary.com/2026/02/14/contract-testing-external-apis-in-php-with-pact-real-laravel-example/">Contract Testing External APIs in PHP with Pact (Real Laravel Example)</a> appeared first on <a href="https://codecraftdiary.com">CodeCraft Diary</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://codecraftdiary.com/2026/02/14/contract-testing-external-apis-in-php-with-pact-real-laravel-example/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Mocking External APIs in PHP (Sandbox Example)</title>
		<link>https://codecraftdiary.com/2026/01/24/mocking-external-apis-in-php-sandbox-example/</link>
					<comments>https://codecraftdiary.com/2026/01/24/mocking-external-apis-in-php-sandbox-example/#respond</comments>
		
		<dc:creator><![CDATA[codecraftdiary]]></dc:creator>
		<pubDate>Sat, 24 Jan 2026 10:32:28 +0000</pubDate>
				<category><![CDATA[Testing]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[mocking]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[testing]]></category>
		<guid isPermaLink="false">https://codecraftdiary.com/?p=3151</guid>

					<description><![CDATA[<p>Testing external API integrations thoroughly is one of the hardest parts of building reliable web applications. Live API calls are slow, flaky, costly, and can fail unpredictably due to network issues, rate limits, or provider outages. For these reasons, most mature test suites do not make real HTTP requests — they mock them. This article [&#8230;]</p>
<p>The post <a href="https://codecraftdiary.com/2026/01/24/mocking-external-apis-in-php-sandbox-example/">Mocking External APIs in PHP (Sandbox Example)</a> appeared first on <a href="https://codecraftdiary.com">CodeCraft Diary</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Testing external API integrations thoroughly is one of the hardest parts of building reliable web applications. Live API calls are slow, flaky, costly, and can fail unpredictably due to network issues, rate limits, or provider outages. For these reasons, most mature test suites <strong>do not make real HTTP requests</strong> — they mock them.</p>



<p class="wp-block-paragraph">This article walks you through <strong>mocking external HTTP APIs in plain PHP</strong>, from basics to advanced techniques, with sandbox-style examples you can run immediately. We’ll cover:</p>



<ul class="wp-block-list">
<li>Why mocking is essential</li>



<li>Creating a PHP API client</li>



<li>Mocking responses with Guzzle</li>



<li>Dynamic and conditional fake responses</li>



<li>Simulating error conditions (timeouts, server errors)</li>



<li>Asserting requests were made correctly</li>



<li>Sequential responses for testing retries</li>



<li>Using sandbox APIs for integration</li>
</ul>



<p class="wp-block-paragraph">Previous article in testing category: <a href="https://codecraftdiary.com/2026/01/03/testing-database-logic-what-to-test-what-to-skip-and-why-it-matters/">https://codecraftdiary.com/2026/01/03/testing-database-logic-what-to-test-what-to-skip-and-why-it-matters/</a></p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading has-text-color has-link-color wp-elements-79ac42ad7a2944944ed30b58ee2574b0" style="color:#0092eb"><strong>1. Why Mock External API Calls</strong></h2>



<p class="wp-block-paragraph">Before diving into code, here’s why mocking is crucial:</p>



<p class="wp-block-paragraph"><strong>a. Speed</strong><br>Live HTTP calls dramatically slow down tests — often orders of magnitude slower than in-memory logic.</p>



<p class="wp-block-paragraph"><strong>b. Stability</strong><br>Third-party APIs can be unreliable, returning downtime or throttling, which makes tests flaky.</p>



<p class="wp-block-paragraph"><strong>c. Isolation</strong><br>Tests should verify your application logic, not the availability or correctness of external services.</p>



<p class="wp-block-paragraph"><strong>d. Cost and Limits</strong><br>Calling paid APIs every test run can incur costs or hit rate limits.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading has-text-color has-link-color wp-elements-e1e39d539cc4897e599af1b522190ce0" style="color:#0092eb"><strong>2. Creating a PHP HTTP Client</strong></h2>



<p class="wp-block-paragraph">We’ll use <strong>Guzzle</strong> as our HTTP client. Here&#8217;s a simple <code>WeatherClient</code>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-Roboto-Mono.ttf" style="font-size:1rem;font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * 1rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;?php
require 'vendor/autoload.php';

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

class WeatherClient
{
    private Client $client;
    private string $baseUrl;
    private string $apiKey;

    public function __construct(string $baseUrl, string $apiKey)
    {
        $this->baseUrl = $baseUrl;
        $this->apiKey = $apiKey;
        $this->client = new Client(&#91;'base_uri' => $baseUrl&#93;);
    }

    public function getCurrent(string $city): array
    {
        try {
            $response = $this->client->request('GET', '/current', [
                'query' => &#91;'q' => $city, 'key' => $this->apiKey&#93;,
                'http_errors' => false
            ]);
            return json_decode($response->getBody()->getContents(), true);
        } catch (RequestException $e) {
            throw new \RuntimeException("API request failed: ".$e->getMessage());
        }
    }
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&lt;?php</span></span>
<span class="line"><span style="color: #C586C0">require</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&#39;vendor/autoload.php&#39;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">use</span><span style="color: #D4D4D4"> GuzzleHttp\</span><span style="color: #4EC9B0">Client</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #569CD6">use</span><span style="color: #D4D4D4"> GuzzleHttp\Exception\</span><span style="color: #4EC9B0">RequestException</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">WeatherClient</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Client</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$client</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$baseUrl</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$apiKey</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">__construct</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$baseUrl</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$apiKey</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">baseUrl</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">$baseUrl</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">apiKey</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">$apiKey</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">client</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Client</span><span style="color: #D4D4D4">(&#91;</span><span style="color: #CE9178">&#39;base_uri&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #9CDCFE">$baseUrl</span><span style="color: #D4D4D4">&#93;);</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">getCurrent</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$city</span><span style="color: #D4D4D4">): </span><span style="color: #569CD6">array</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">try</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">$response</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">client</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">request</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39;GET&#39;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&#39;/current&#39;</span><span style="color: #D4D4D4">, [</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #CE9178">&#39;query&#39;</span><span style="color: #D4D4D4"> =&gt; &#91;</span><span style="color: #CE9178">&#39;q&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #9CDCFE">$city</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&#39;key&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">apiKey</span><span style="color: #D4D4D4">&#93;,</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #CE9178">&#39;http_errors&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #569CD6">false</span></span>
<span class="line"><span style="color: #D4D4D4">            ]);</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">json_decode</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$response</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">getBody</span><span style="color: #D4D4D4">()-&gt;</span><span style="color: #DCDCAA">getContents</span><span style="color: #D4D4D4">(), </span><span style="color: #569CD6">true</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">        } </span><span style="color: #C586C0">catch</span><span style="color: #D4D4D4"> (</span><span style="color: #4EC9B0">RequestException</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$e</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">throw</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">\RuntimeException</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;API request failed: &quot;</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">$e</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">getMessage</span><span style="color: #D4D4D4">());</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre><span style="display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-start;background-color:#1E1E1E;color:#c7c7c7;font-size:12px;line-height:1;position:relative">PHP</span></div>



<ul class="wp-block-list">
<li><code>GuzzleHttp\Client</code> handles HTTP requests</li>



<li>Works in plain PHP without Laravel</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading has-text-color has-link-color wp-elements-7389349078e6fe9222bf42a0544d1621" style="color:#0092eb"><strong>3. Mocking API Responses with Guzzle</strong></h2>



<p class="wp-block-paragraph">Guzzle provides <strong>MockHandler</strong> to simulate responses. Here’s a basic test:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-Roboto-Mono.ttf" style="font-size:1rem;font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * 1rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;?php
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;

// Mock API response
$mock = new MockHandler([
    new Response(200, [], json_encode([
        'location' => &#91;'name' => 'London'&#93;,
        'current' => &#91;'temp_c' => 18&#93;
    ]))
]);

$handlerStack = HandlerStack::create($mock);
$client = new Client(&#91;'handler' => $handlerStack, 'base_uri' => 'https://api.weather.com'&#93;);

// Inject mock client into WeatherClient
$weatherClient = new WeatherClient('https://api.weather.com', 'dummy-key');
$ref = new ReflectionClass($weatherClient);
$prop = $ref->getProperty('client');
$prop->setAccessible(true);
$prop->setValue($weatherClient, $client);

// Make request (uses mocked response)
$result = $weatherClient->getCurrent('London');

echo "City: ".$result&#91;'location'&#93;&#91;'name'&#93;."\n";   // London
echo "Temperature: ".$result&#91;'current'&#93;&#91;'temp_c'&#93;."°C\n"; // 18
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&lt;?php</span></span>
<span class="line"><span style="color: #569CD6">use</span><span style="color: #D4D4D4"> GuzzleHttp\</span><span style="color: #4EC9B0">Client</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #569CD6">use</span><span style="color: #D4D4D4"> GuzzleHttp\Handler\</span><span style="color: #4EC9B0">MockHandler</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #569CD6">use</span><span style="color: #D4D4D4"> GuzzleHttp\</span><span style="color: #4EC9B0">HandlerStack</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #569CD6">use</span><span style="color: #D4D4D4"> GuzzleHttp\Psr7\</span><span style="color: #4EC9B0">Response</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">// Mock API response</span></span>
<span class="line"><span style="color: #9CDCFE">$mock</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">MockHandler</span><span style="color: #D4D4D4">([</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Response</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">200</span><span style="color: #D4D4D4">, [], </span><span style="color: #DCDCAA">json_encode</span><span style="color: #D4D4D4">([</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #CE9178">&#39;location&#39;</span><span style="color: #D4D4D4"> =&gt; &#91;</span><span style="color: #CE9178">&#39;name&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #CE9178">&#39;London&#39;</span><span style="color: #D4D4D4">&#93;,</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #CE9178">&#39;current&#39;</span><span style="color: #D4D4D4"> =&gt; &#91;</span><span style="color: #CE9178">&#39;temp_c&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #B5CEA8">18</span><span style="color: #D4D4D4">&#93;</span></span>
<span class="line"><span style="color: #D4D4D4">    ]))</span></span>
<span class="line"><span style="color: #D4D4D4">]);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #9CDCFE">$handlerStack</span><span style="color: #D4D4D4"> = </span><span style="color: #4EC9B0">HandlerStack</span><span style="color: #D4D4D4">::</span><span style="color: #DCDCAA">create</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$mock</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #9CDCFE">$client</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Client</span><span style="color: #D4D4D4">(&#91;</span><span style="color: #CE9178">&#39;handler&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #9CDCFE">$handlerStack</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&#39;base_uri&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #CE9178">&#39;https://api.weather.com&#39;</span><span style="color: #D4D4D4">&#93;);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">// Inject mock client into WeatherClient</span></span>
<span class="line"><span style="color: #9CDCFE">$weatherClient</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">WeatherClient</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39;https://api.weather.com&#39;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&#39;dummy-key&#39;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #9CDCFE">$ref</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">ReflectionClass</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$weatherClient</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #9CDCFE">$prop</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">$ref</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">getProperty</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39;client&#39;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #9CDCFE">$prop</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">setAccessible</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">true</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #9CDCFE">$prop</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">setValue</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$weatherClient</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">$client</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">// Make request (uses mocked response)</span></span>
<span class="line"><span style="color: #9CDCFE">$result</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">$weatherClient</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">getCurrent</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39;London&#39;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #DCDCAA">echo</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;City: &quot;</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">$result</span><span style="color: #D4D4D4">&#91;</span><span style="color: #CE9178">&#39;location&#39;</span><span style="color: #D4D4D4">&#93;&#91;</span><span style="color: #CE9178">&#39;name&#39;</span><span style="color: #D4D4D4">&#93;</span><span style="color: #D4D4D4">.</span><span style="color: #CE9178">&quot;</span><span style="color: #D7BA7D">\n</span><span style="color: #CE9178">&quot;</span><span style="color: #D4D4D4">;   </span><span style="color: #6A9955">// London</span></span>
<span class="line"><span style="color: #DCDCAA">echo</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;Temperature: &quot;</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">$result</span><span style="color: #D4D4D4">&#91;</span><span style="color: #CE9178">&#39;current&#39;</span><span style="color: #D4D4D4">&#93;&#91;</span><span style="color: #CE9178">&#39;temp_c&#39;</span><span style="color: #D4D4D4">&#93;</span><span style="color: #D4D4D4">.</span><span style="color: #CE9178">&quot;°C</span><span style="color: #D7BA7D">\n</span><span style="color: #CE9178">&quot;</span><span style="color: #D4D4D4">; </span><span style="color: #6A9955">// 18</span></span>
<span class="line"></span></code></pre><span style="display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-start;background-color:#1E1E1E;color:#c7c7c7;font-size:12px;line-height:1;position:relative">PHP</span></div>



<ul class="wp-block-list">
<li>No real HTTP requests are made</li>



<li>Ideal for unit testing</li>
</ul>



<p class="wp-block-paragraph"></p>



<h3 class="wp-block-heading has-text-color has-link-color wp-elements-aee9399c3e6f9b0f18c2197a609a2018" style="color:#0092eb"><strong>3a. Basic Example: Mocking a Simple API in Plain PHP Sandbox</strong></h3>



<p class="wp-block-paragraph">This version shows how to simulate external API responses in <strong>plain PHP</strong>, without Guzzle or Laravel, making it suitable for online sandbox environments like <a href="https://onlinephp.io/c/d4940" target="_blank" rel="noreferrer noopener">onlinephp.io.</a></p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-Roboto-Mono.ttf" style="font-size:1rem;font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * 1rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;?php
// Simple WeatherClient that returns fake responses
class WeatherClient
{
    private $baseUrl;
    private $apiKey;

    public function __construct(string $baseUrl, string $apiKey)
    {
        $this->baseUrl = $baseUrl;
        $this->apiKey = $apiKey;
    }

    // Fake API call returning predefined data
    public function getCurrent(string $city): array
    {
        $fakeApiResponses = [
            'London' => [
                'location' => &#91;'name' => 'London'&#93;,
                'current' => &#91;'temp_c' => 18, 'condition' => 'Partly cloudy'&#93;
            ],
            'New York' => [
                'location' => &#91;'name' => 'New York'&#93;,
                'current' => &#91;'temp_c' => 22, 'condition' => 'Sunny'&#93;
            ]
        ];

        return $fakeApiResponses&#91;$city&#93; ?? [
            'location' => &#91;'name' => $city&#93;,
            'current' => &#91;'temp_c' => null, 'condition' => 'Unknown'&#93;
        ];
    }
}

// --- Sandbox Test ---
$client = new WeatherClient('https://api.fakeweather.com', 'dummy-key');

$cities = &#91;'London', 'New York', 'Paris'&#93;;

foreach ($cities as $city) {
    $result = $client->getCurrent($city);
    $temp = $result&#91;'current'&#93;&#91;'temp_c'&#93; ?? 'unknown';
    $condition = $result&#91;'current'&#93;&#91;'condition'&#93; ?? 'unknown';
    echo "City: {$result&#91;'location'&#93;&#91;'name'&#93;}, Temperature: {$temp}°C, Condition: {$condition}\n";
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&lt;?php</span></span>
<span class="line"><span style="color: #6A9955">// Simple WeatherClient that returns fake responses</span></span>
<span class="line"><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">WeatherClient</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$baseUrl</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$apiKey</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">__construct</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$baseUrl</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$apiKey</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">baseUrl</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">$baseUrl</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">apiKey</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">$apiKey</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// Fake API call returning predefined data</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">getCurrent</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$city</span><span style="color: #D4D4D4">): </span><span style="color: #569CD6">array</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">$fakeApiResponses</span><span style="color: #D4D4D4"> = [</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #CE9178">&#39;London&#39;</span><span style="color: #D4D4D4"> =&gt; [</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #CE9178">&#39;location&#39;</span><span style="color: #D4D4D4"> =&gt; &#91;</span><span style="color: #CE9178">&#39;name&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #CE9178">&#39;London&#39;</span><span style="color: #D4D4D4">&#93;,</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #CE9178">&#39;current&#39;</span><span style="color: #D4D4D4"> =&gt; &#91;</span><span style="color: #CE9178">&#39;temp_c&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #B5CEA8">18</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&#39;condition&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #CE9178">&#39;Partly cloudy&#39;</span><span style="color: #D4D4D4">&#93;</span></span>
<span class="line"><span style="color: #D4D4D4">            ],</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #CE9178">&#39;New York&#39;</span><span style="color: #D4D4D4"> =&gt; [</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #CE9178">&#39;location&#39;</span><span style="color: #D4D4D4"> =&gt; &#91;</span><span style="color: #CE9178">&#39;name&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #CE9178">&#39;New York&#39;</span><span style="color: #D4D4D4">&#93;,</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #CE9178">&#39;current&#39;</span><span style="color: #D4D4D4"> =&gt; &#91;</span><span style="color: #CE9178">&#39;temp_c&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #B5CEA8">22</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&#39;condition&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #CE9178">&#39;Sunny&#39;</span><span style="color: #D4D4D4">&#93;</span></span>
<span class="line"><span style="color: #D4D4D4">            ]</span></span>
<span class="line"><span style="color: #D4D4D4">        ];</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$fakeApiResponses</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">$city</span><span style="color: #D4D4D4">&#93; ?? [</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #CE9178">&#39;location&#39;</span><span style="color: #D4D4D4"> =&gt; &#91;</span><span style="color: #CE9178">&#39;name&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #9CDCFE">$city</span><span style="color: #D4D4D4">&#93;,</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #CE9178">&#39;current&#39;</span><span style="color: #D4D4D4"> =&gt; &#91;</span><span style="color: #CE9178">&#39;temp_c&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #569CD6">null</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&#39;condition&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #CE9178">&#39;Unknown&#39;</span><span style="color: #D4D4D4">&#93;</span></span>
<span class="line"><span style="color: #D4D4D4">        ];</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">// --- Sandbox Test ---</span></span>
<span class="line"><span style="color: #9CDCFE">$client</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">WeatherClient</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39;https://api.fakeweather.com&#39;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&#39;dummy-key&#39;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #9CDCFE">$cities</span><span style="color: #D4D4D4"> = &#91;</span><span style="color: #CE9178">&#39;London&#39;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&#39;New York&#39;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&#39;Paris&#39;</span><span style="color: #D4D4D4">&#93;;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C586C0">foreach</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$cities</span><span style="color: #D4D4D4"> as </span><span style="color: #9CDCFE">$city</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$result</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">$client</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">getCurrent</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$city</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$temp</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">$result</span><span style="color: #D4D4D4">&#91;</span><span style="color: #CE9178">&#39;current&#39;</span><span style="color: #D4D4D4">&#93;&#91;</span><span style="color: #CE9178">&#39;temp_c&#39;</span><span style="color: #D4D4D4">&#93; ?? </span><span style="color: #CE9178">&#39;unknown&#39;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$condition</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">$result</span><span style="color: #D4D4D4">&#91;</span><span style="color: #CE9178">&#39;current&#39;</span><span style="color: #D4D4D4">&#93;&#91;</span><span style="color: #CE9178">&#39;condition&#39;</span><span style="color: #D4D4D4">&#93; ?? </span><span style="color: #CE9178">&#39;unknown&#39;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">echo</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;City: {</span><span style="color: #9CDCFE">$result</span><span style="color: #CE9178">&#91;&#39;location&#39;&#93;&#91;&#39;name&#39;&#93;}, Temperature: {</span><span style="color: #9CDCFE">$temp</span><span style="color: #CE9178">}°C, Condition: {</span><span style="color: #9CDCFE">$condition</span><span style="color: #CE9178">}</span><span style="color: #D7BA7D">\n</span><span style="color: #CE9178">&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre><span style="display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-start;background-color:#1E1E1E;color:#c7c7c7;font-size:12px;line-height:1;position:relative">PHP</span></div>



<p class="wp-block-paragraph"><strong>Why include this:</strong></p>



<ul class="wp-block-list">
<li>Shows a <strong>fully runnable example</strong> in any PHP environment.</li>



<li>Illustrates the concept of <strong>mocking/faking API responses</strong> without external dependencies.</li>



<li>Complements the Guzzle-based example for readers who want a <strong>quick sandbox-ready approach</strong>.</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading has-text-color has-link-color wp-elements-d53e8f5e14f21df1a2996ca6578365cd" style="color:#0092eb"><strong>4. Dynamic and Conditional Fake Responses</strong></h2>



<p class="wp-block-paragraph">You can customize responses based on input:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-Roboto-Mono.ttf" style="font-size:1rem;font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * 1rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>$mock = new MockHandler([
    function ($request, $options) {
        parse_str($request->getUri()->getQuery(), $query);
        if ($query&#91;'q'&#93; === 'US') {
            return new Response(200, [], json_encode(&#91;'data'=>'US result'&#93;));
        }
        return new Response(200, [], json_encode(&#91;'data'=>'Other result'&#93;));
    }
]);

$handlerStack = HandlerStack::create($mock);
$client = new Client(&#91;'handler' => $handlerStack&#93;);
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #9CDCFE">$mock</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">MockHandler</span><span style="color: #D4D4D4">([</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$request</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">$options</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #DCDCAA">parse_str</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$request</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">getUri</span><span style="color: #D4D4D4">()-&gt;</span><span style="color: #DCDCAA">getQuery</span><span style="color: #D4D4D4">(), </span><span style="color: #9CDCFE">$query</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$query</span><span style="color: #D4D4D4">&#91;</span><span style="color: #CE9178">&#39;q&#39;</span><span style="color: #D4D4D4">&#93; === </span><span style="color: #CE9178">&#39;US&#39;</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Response</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">200</span><span style="color: #D4D4D4">, [], </span><span style="color: #DCDCAA">json_encode</span><span style="color: #D4D4D4">(&#91;</span><span style="color: #CE9178">&#39;data&#39;</span><span style="color: #D4D4D4">=&gt;</span><span style="color: #CE9178">&#39;US result&#39;</span><span style="color: #D4D4D4">&#93;));</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Response</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">200</span><span style="color: #D4D4D4">, [], </span><span style="color: #DCDCAA">json_encode</span><span style="color: #D4D4D4">(&#91;</span><span style="color: #CE9178">&#39;data&#39;</span><span style="color: #D4D4D4">=&gt;</span><span style="color: #CE9178">&#39;Other result&#39;</span><span style="color: #D4D4D4">&#93;));</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">]);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #9CDCFE">$handlerStack</span><span style="color: #D4D4D4"> = </span><span style="color: #4EC9B0">HandlerStack</span><span style="color: #D4D4D4">::</span><span style="color: #DCDCAA">create</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$mock</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #9CDCFE">$client</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Client</span><span style="color: #D4D4D4">(&#91;</span><span style="color: #CE9178">&#39;handler&#39;</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #9CDCFE">$handlerStack</span><span style="color: #D4D4D4">&#93;);</span></span>
<span class="line"></span></code></pre><span style="display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-start;background-color:#1E1E1E;color:#c7c7c7;font-size:12px;line-height:1;position:relative">PHP</span></div>



<ul class="wp-block-list">
<li>Use closures to decide response dynamically</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading has-text-color has-link-color wp-elements-c80505c8c7f2b0b30642e9fe5d77f481" style="color:#0092eb"><strong>5. Simulating Errors: Timeouts &amp; Server Failures</strong></h2>



<p class="wp-block-paragraph"><strong>a. Failed Connection / Timeout</strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-Roboto-Mono.ttf" style="font-size:1rem;font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * 1rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>$mock = new MockHandler(&#91;
    new \GuzzleHttp\Exception\ConnectException(
        "Connection failed",
        new \GuzzleHttp\Psr7\Request('GET', '/current')
    )
&#93;);
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #9CDCFE">$mock</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">MockHandler</span><span style="color: #D4D4D4">(&#91;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> \GuzzleHttp\Exception\</span><span style="color: #4EC9B0">ConnectException</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #CE9178">&quot;Connection failed&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> \GuzzleHttp\Psr7\</span><span style="color: #4EC9B0">Request</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39;GET&#39;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&#39;/current&#39;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    )</span></span>
<span class="line"><span style="color: #D4D4D4">&#93;);</span></span>
<span class="line"></span></code></pre><span style="display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-start;background-color:#1E1E1E;color:#c7c7c7;font-size:12px;line-height:1;position:relative">PHP</span></div>



<p class="wp-block-paragraph"><strong>b. Server Errors (HTTP 500)</strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-Roboto-Mono.ttf" style="font-size:1rem;font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * 1rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>$mock = new MockHandler([
    new Response(500, [], 'Internal Server Error')
]);
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #9CDCFE">$mock</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">MockHandler</span><span style="color: #D4D4D4">([</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Response</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">500</span><span style="color: #D4D4D4">, [], </span><span style="color: #CE9178">&#39;Internal Server Error&#39;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">]);</span></span>
<span class="line"></span></code></pre><span style="display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-start;background-color:#1E1E1E;color:#c7c7c7;font-size:12px;line-height:1;position:relative">PHP</span></div>



<p class="wp-block-paragraph"><strong>c. Sequential Responses for Retry Logic</strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-Roboto-Mono.ttf" style="font-size:1rem;font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * 1rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>$mock = new MockHandler([
    new Response(500, [], 'Fail'),  // First request
    new Response(200, [], json_encode(['location'=>&#91;'name'=>'London'&#93;, 'current'=>&#91;'temp_c'=>20&#93;])), // Second
]);
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #9CDCFE">$mock</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">MockHandler</span><span style="color: #D4D4D4">([</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Response</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">500</span><span style="color: #D4D4D4">, [], </span><span style="color: #CE9178">&#39;Fail&#39;</span><span style="color: #D4D4D4">),  </span><span style="color: #6A9955">// First request</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Response</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">200</span><span style="color: #D4D4D4">, [], </span><span style="color: #DCDCAA">json_encode</span><span style="color: #D4D4D4">([</span><span style="color: #CE9178">&#39;location&#39;</span><span style="color: #D4D4D4">=&gt;&#91;</span><span style="color: #CE9178">&#39;name&#39;</span><span style="color: #D4D4D4">=&gt;</span><span style="color: #CE9178">&#39;London&#39;</span><span style="color: #D4D4D4">&#93;, </span><span style="color: #CE9178">&#39;current&#39;</span><span style="color: #D4D4D4">=&gt;&#91;</span><span style="color: #CE9178">&#39;temp_c&#39;</span><span style="color: #D4D4D4">=&gt;</span><span style="color: #B5CEA8">20</span><span style="color: #D4D4D4">&#93;])), </span><span style="color: #6A9955">// Second</span></span>
<span class="line"><span style="color: #D4D4D4">]);</span></span>
<span class="line"></span></code></pre><span style="display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-start;background-color:#1E1E1E;color:#c7c7c7;font-size:12px;line-height:1;position:relative">PHP</span></div>



<ul class="wp-block-list">
<li>Useful for testing retry behavior</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading has-text-color has-link-color wp-elements-6b470a6888812adf68f0e1f5165a01c4" style="color:#0092eb"><strong>6. Assertions</strong></h2>



<p class="wp-block-paragraph">In PHPUnit, you can assert the results:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-Roboto-Mono.ttf" style="font-size:1rem;font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * 1rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>$this->assertEquals('London', $result&#91;'location'&#93;&#91;'name'&#93;);
$this->assertEquals(18, $result&#91;'current'&#93;&#91;'temp_c'&#93;);
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">assertEquals</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39;London&#39;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">$result</span><span style="color: #D4D4D4">&#91;</span><span style="color: #CE9178">&#39;location&#39;</span><span style="color: #D4D4D4">&#93;&#91;</span><span style="color: #CE9178">&#39;name&#39;</span><span style="color: #D4D4D4">&#93;);</span></span>
<span class="line"><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">assertEquals</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">18</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">$result</span><span style="color: #D4D4D4">&#91;</span><span style="color: #CE9178">&#39;current&#39;</span><span style="color: #D4D4D4">&#93;&#91;</span><span style="color: #CE9178">&#39;temp_c&#39;</span><span style="color: #D4D4D4">&#93;);</span></span>
<span class="line"></span></code></pre><span style="display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-start;background-color:#1E1E1E;color:#c7c7c7;font-size:12px;line-height:1;position:relative">PHP</span></div>



<p class="wp-block-paragraph">In a sandbox without PHPUnit, simple checks:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-Roboto-Mono.ttf" style="font-size:1rem;font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * 1rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>if ($result&#91;'location'&#93;&#91;'name'&#93; === 'London') {
    echo "Test passed!\n";
} else {
    echo "Test failed!\n";
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$result</span><span style="color: #D4D4D4">&#91;</span><span style="color: #CE9178">&#39;location&#39;</span><span style="color: #D4D4D4">&#93;&#91;</span><span style="color: #CE9178">&#39;name&#39;</span><span style="color: #D4D4D4">&#93; === </span><span style="color: #CE9178">&#39;London&#39;</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">echo</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;Test passed!</span><span style="color: #D7BA7D">\n</span><span style="color: #CE9178">&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">} </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">echo</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;Test failed!</span><span style="color: #D7BA7D">\n</span><span style="color: #CE9178">&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre><span style="display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-start;background-color:#1E1E1E;color:#c7c7c7;font-size:12px;line-height:1;position:relative">PHP</span></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading has-text-color has-link-color wp-elements-f42791f02205f6eae1aeba39403afc96" style="color:#0092eb"><strong>7. Using Fixtures for Realistic Responses</strong></h2>



<p class="wp-block-paragraph">Instead of hardcoding JSON:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-Roboto-Mono.ttf" style="font-size:1rem;font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * 1rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>$body = file_get_contents('weather_fixture.json');
$mock = new MockHandler([ new Response(200, [], $body) ]);
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #9CDCFE">$body</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">file_get_contents</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39;weather_fixture.json&#39;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #9CDCFE">$mock</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">MockHandler</span><span style="color: #D4D4D4">([ </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Response</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">200</span><span style="color: #D4D4D4">, [], </span><span style="color: #9CDCFE">$body</span><span style="color: #D4D4D4">) ]);</span></span>
<span class="line"></span></code></pre><span style="display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-start;background-color:#1E1E1E;color:#c7c7c7;font-size:12px;line-height:1;position:relative">PHP</span></div>



<ul class="wp-block-list">
<li>Easier to maintain and mirrors real API structure</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading has-text-color has-link-color wp-elements-d19c0036cbe9040766a823b985c9891c" style="color:#0092eb"><strong>8. Integration Testing with Sandbox APIs</strong></h2>



<p class="wp-block-paragraph">Some APIs provide <strong>sandbox environments</strong> (Stripe, PayPal, GitHub) where you can test real HTTP requests safely.</p>



<p class="wp-block-paragraph">For full sandbox testing:</p>



<ul class="wp-block-list">
<li>Configure your client’s base URL to the sandbox</li>



<li>Optionally, use <strong>Wiremock</strong> or similar mock servers for advanced stubbing</li>



<li>Run integration tests against sandbox to ensure real API compatibility</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading has-text-color has-link-color wp-elements-50e06f484af5e6dd06648c14657fd608" style="color:#0092eb"><strong>9. Best Practices</strong></h2>



<ul class="wp-block-list">
<li>Mock <strong>external APIs only</strong>, don’t mock internal logic</li>



<li>Use <strong>descriptive fixtures</strong> for clarity</li>



<li>Include <strong>error scenarios</strong> in tests (timeouts, 500 errors)</li>



<li>Combine <strong>unit tests (mocked)</strong> with occasional <strong>sandbox integration tests</strong></li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://codecraftdiary.com/2026/01/24/mocking-external-apis-in-php-sandbox-example/">Mocking External APIs in PHP (Sandbox Example)</a> appeared first on <a href="https://codecraftdiary.com">CodeCraft Diary</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://codecraftdiary.com/2026/01/24/mocking-external-apis-in-php-sandbox-example/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
