Laravel API testen met stubs

Artikel hoe je een stub maakt en gebruikt in een Laravel feature test met een API call.
Als je een API wil testen in een feature test wil je niet de echt API aanroepen maar gebruik maar het faken. Dit omdat je niets steeds een API kan/wil aanroepen het maakt je test sneller.

In dit artikel maak ik gebruik van van een dummy (stub) bestand met daarin de response van een API request.
In dit voorbeeld ‘stub’ ik een Woocommerce product en maak een feature test waarin je zo’n stub bestand kan toepassen.

Stub file maken

Ik gebruik hier zelf een kleine helper functie voor om een dummy/stub bestanden te genereren.

<!--?php 
function createTestStub($content, $filename):void {
  \Illuminate\Support\Facades\File::put(base_path('tests/Stubs/' . $filename), $content);
}
</pre>
<p>Ik doe nu een echte API call, als voorbeeld een call om een product van Woocommerce webshop op te halen.<br ?-->
Die data sla ik dan op in een stub bestand om te kunnen gebruiken in een test.
<!--?php $response = Http::withBasicAuth('userName', 'passWord') -&gt;baseUrl('https://url-to-woocommerce.com/wp-json/wc/v3/')
                -&gt;get('products/1914');

if ($response-&gt;status() == 200) {
  createTestStub($response-&gt;body(), 'Woocommerce/TestProducts.json');
}           
</pre>
<h2>Route en controller functies om een product op te halen / opslaan</h2>
<p>Hierbij een voorbeeld wat test of we een product vanuit een API call kunnen opslaan in de database.</p>
<h3>Route bestand</h3>
<p>In mijn route file een post route</p>
<pre>&lt;?php Route::post('store-product/{id}', [\App\Http\Controllers\ProductsController::class, 'storeProduct'])-&gt;name('storeProduct');
</pre>
<h3>ProductsController, method StoreProduct()</h3>
<pre>&lt;&lt;?php public function storeProduct(Request $request) { $response = Http::withBasicAuth('userName', 'passWord') -&gt;baseUrl('https://url-to-woocommerce.com/wp-json/wc/v3/')
        -&gt;get('products/' . $request-&gt;get('productId'));

    if ($response-&gt;status() == 200) {

        $product = json_decode($response-&gt;body());

        // Hier kunnen we nog validatie doen maar dat is weer voor een ander artikel.

        return Product::create([
            'name' =&gt; $product-&gt;name,
            'slug' =&gt; $product-&gt;slug,
            // hier de rest van het product wat we willen opslaan.
        ]);
    }
    return response(json_decode($response-&gt;body())-&gt;message, $response-&gt;status());
}
</pre>
<h2>De feature test</h2>
<p>Met onderstaande feature test kunnen we testen of we een product kunnen ophalen en opslaan in onze database.</p>
<pre>
&lt;?php namespace Tests\Feature; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase; class StoreProductTest extends TestCase { use RefreshDatabase; /** * @test */ public function expect_we_can_store_a_product_from_a_woocommerce_api_call_in_our_database() { $this-&gt;post(route('storeProduct'), ['productId' =&gt; 1914])
            -&gt;assertStatus(201);

        $this-&gt;assertDatabaseCount('wc_products', 1);

        $this-&gt;assertDatabaseHas('wc_products', [
            'name' =&gt; 'Product name X',
            'slug' =&gt; 'product-name-x'
        ]);
    }
}
</pre>
<p>Als het goed is zal deze test slagen en je deze uitkomst zien na het runnen van deze test.</p>
<pre>
   PASS  Tests\Feature\StoreProductTest
  ✓ expect we can store a product from a woocommerce api call in our database

  Tests:  1 passed
  Time:   11.31s
</pre>
<p>Alleen doen we in deze test een echte API call. We moeten nu aangeven in de test dat we de API willen faken met de stub file.<br ?-->
Om API calls te kunnen faken kan je gebruik maken van
Http::fake

, meer informatie over stubs, fake, mocken enz. kan je vinden in de documentatie van Laravel.

Voor het gebruiken / ophalen van de gemaakte stub bestand heb ik ook een kleine helper functie.

<!--?php 
function getTestStub($fileName): string {
    return File::get(base_path('tests/Stubs/' . $fileName));
}
</pre>
<p>We zitten deze fake HTTP boven in de test en geven aan dat we het gemaakt stub bestand willen gebruiken.<br ?-->
De test zal er nu zo uitzien:
&lt;?php namespace Tests\Feature; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\Http; use Tests\TestCase; class StoreProductTest extends TestCase { use RefreshDatabase; /** * @test */ public function expect_we_can_store_a_product_from_a_woocommerce_api_call_in_our_database() { Http::fake([ 'https://url-to-woocommerce.com/wp-json/wc/v3/products/1914' =&gt; Http::response(
                getTestStub('Woocommerce/TestProducts.json')
            )
        ]);

        $this-&gt;post(route('storeProduct'), ['productId' =&gt; 1914])
            -&gt;assertStatus(201);

        $this-&gt;assertDatabaseCount('wc_products', 1);

        $this-&gt;assertDatabaseHas('wc_products', [
            'name' =&gt; 'Product name X',
            'slug' =&gt; 'product-name-x'
        ]);
    }
}

De uitkomst is als het goed is hetzelfde, alleen je doet nu geen call meer naar de Woocommerce webshop.