Logo Webbouw Plus

Laravel API testen met stubs

maart 8, 2023

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);
}

Ik doe nu een echte API call, als voorbeeld een call om een product van Woocommerce webshop op te halen.
Die data sla ik dan op in een stub bestand om te kunnen gebruiken in een test.

<?php
$response = Http::withBasicAuth('userName', 'passWord')
                ->baseUrl('https://url-to-woocommerce.com/wp-json/wc/v3/')
                ->get('products/1914');

if ($response->status() == 200) {
  createTestStub($response->body(), 'Woocommerce/TestProducts.json');
}           

Route en controller functies om een product op te halen / opslaan

Hierbij een voorbeeld wat test of we een product vanuit een API call kunnen opslaan in de database.

Route bestand

In mijn route file een post route

<?php
Route::post('store-product/{id}', [\App\Http\Controllers\ProductsController::class, 'storeProduct'])->name('storeProduct');

ProductsController, method StoreProduct()

<<?php
public function storeProduct(Request $request) {

    $response = Http::withBasicAuth('userName', 'passWord')
        ->baseUrl('https://url-to-woocommerce.com/wp-json/wc/v3/')
        ->get('products/' . $request->get('productId'));

    if ($response->status() == 200) {

        $product = json_decode($response->body());

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

        return Product::create([
            'name' => $product->name,
            'slug' => $product->slug,
            // hier de rest van het product wat we willen opslaan.
        ]);
    }
    return response(json_decode($response->body())->message, $response->status());
}

De feature test

Met onderstaande feature test kunnen we testen of we een product kunnen ophalen en opslaan in onze database.


<?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->post(route('storeProduct'), ['productId' => 1914])
            ->assertStatus(201);

        $this->assertDatabaseCount('wc_products', 1);

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

Als het goed is zal deze test slagen en je deze uitkomst zien na het runnen van deze test.


   PASS  Tests\Feature\StoreProductTest
  ✓ expect we can store a product from a woocommerce api call in our database

  Tests:  1 passed
  Time:   11.31s

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.
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));
}

We zitten deze fake HTTP boven in de test en geven aan dat we het gemaakt stub bestand willen gebruiken.
De test zal er nu zo uitzien:

<?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' => Http::response(
                getTestStub('Woocommerce/TestProducts.json')
            )
        ]);

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

        $this->assertDatabaseCount('wc_products', 1);

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

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

WEBBOUWPLUS

Marcel van Doornen - Freelance Laravel developer
Marcel van Doornen, freelance Laravel developer
Ben je benieuwd hoe ik jou kan helpen?
Neem contact met mij op