PHP Academy · Lektion

Mockning och stubbning med Mockery

Isolera enheter med flexibla testdubbletter.

Lektion 2 av 413 steg

Mockning och stubbning med Mockery är en gratis lektion i PHP Academy på CoddyKit. Detta är lektion 2 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för PHP Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i PHP Academy innehåller totalt 4 lektioner.

Varför ett särskilt mockningsbibliotek

PHPUnit levereras med ett eget API för testdubbletter, men Mockery erbjuder en mer flytande och uttrycksfull syntax samt funktioner som PHPUnits mockar saknar — partiella mockar, spioner, flexibel argumentmatchning och ordnade förväntningar. För kod med komplexa samarbeten blir Mockery ofta betydligt lättare att läsa. I den här lektionen behandlas hela spektrumet av dubbletter och när de bör användas.

composer require --dev mockery/mockery

Stub, mock eller spy

Exakt vokabulär förebygger förvirrade tester:

  • Stub — returnerar förutbestämda värden; ni verifierar tillstånd (resultatet).
  • Mock — har förväntningar på anrop; ni verifierar beteende (att den anropades korrekt). Om en förväntning inte uppfylls misslyckas testet.
  • Spy — registrerar anrop och låter er verifiera dem i efterhand.

Tumregel: föredra stubbar för frågor och mockar/spies för kommandon.

En stub med allows()

Använd Mockery::mock() och allows() (eller shouldReceive()->andReturn()) för att tillhandahålla förutbestämda returvärden utan att verifiera att anropet gjordes. Här levererar en stub för en gateway en känd växelkurs, så att vi kan testa beräkningen isolerat.

<?php
use Mockery;
use PHPUnit\Framework\TestCase;

final class ConverterTest extends TestCase
{
    public function test_converts_using_rate(): void
    {
        $rates = Mockery::mock(RateGateway::class);
        $rates->allows()->rateFor('USD', 'EUR')->andReturn(0.9);

        $sut = new Converter($rates);
        self::assertSame(90.0, $sut->convert(100.0, 'USD', 'EUR'));

        Mockery::close();
    }
}

En mock med expects()

När själva interaktionen är kontraktet — till exempel att "ett e-postmeddelande måste skickas exakt en gång" — använder ni expects() eller shouldReceive()->once(). Mockery verifierar förväntningen under Mockery::close(); ett uteblivet anrop får testet att misslyckas.

<?php
use Mockery;

$mailer = Mockery::mock(Mailer::class);
$mailer->expects()
       ->send(Mockery::type(Message::class))
       ->once();

$service = new SignupService($mailer);
$service->register('ada@example.com');

Mockery::close(); // fails here if send() was never called

Argumentmatchare

Mockerys matchare låter er göra förväntningar så lösa eller strikta som behövs:

  • Mockery::any() — vilket värde som helst.
  • Mockery::type('string') / ett klassnamn — typkontroll.
  • Mockery::on(fn($a) => ...) — eget predikat.
  • Mockery::capture($var) — fånga argumentet för senare verifiering.
<?php
use Mockery;

$repo = Mockery::mock(UserRepo::class);
$repo->expects()
     ->save(Mockery::on(fn(User $u) => $u->isActive()))
     ->once()
     ->andReturnTrue();

Retursekvenser och dynamiska returer

Ni kan ange flera returvärden för efterföljande anrop eller beräkna returen utifrån argumenten med andReturnUsing(). Det modellerar logik för återförsök, sidindelning eller tillståndsberoende samarbetspartners.

<?php
use Mockery;

$api = Mockery::mock(HttpClient::class);
// First call throws, second succeeds (retry test):
$api->shouldReceive('get')
    ->twice()
    ->andThrow(new \RuntimeException('timeout'))
    ->andReturn('{"ok":true}');

// Or derive the return from the input:
$api->shouldReceive('echo')
    ->andReturnUsing(fn(string $in) => strtoupper($in));

Spies: verifiera i efterhand

En spy vänder på ordningen: agera först och verifiera sedan. Mockery::spy() registrerar anrop; därefter frågar ni efter dem med shouldHaveReceived(). Spies håller Arrange–Act–Assert-strukturen ren när ni inte vill att förinställda förväntningar ska belamra förberedelserna.

<?php
use Mockery;

$logger = Mockery::spy(Logger::class);

$service = new PaymentService($logger);
$service->charge(500);

// Assertions happen AFTER the action:
$logger->shouldHaveReceived('info')
       ->with(Mockery::pattern('/charged 500/'))
       ->once();

Mockery::close();

Partiella mockar

Ibland vill ni ha ett riktigt objekt men skriva över en metod — en partiell mock. Mockerys makePartial() anropar de riktiga metoderna, utom de metoder som ni har satt förväntningar på. Använd detta sparsamt: ett stort beroende av partiella mockar signalerar ofta att en klass gör för mycket.

<?php
use Mockery;

$report = Mockery::mock(Report::class)->makePartial();
// Only stub the slow/external method; the rest runs for real
$report->shouldReceive('fetchRawData')->andReturn(['a', 'b', 'c']);

// real summarize() runs, using the stubbed data
$summary = $report->summarize();

Mocka inte sådant ni inte äger

En grundläggande testprincip är att undvika att mocka tredjepartsklasser direkt. Deras API:er kan ändras, och er mock glider då omärkligt bort från verkligheten. Omslut dem i stället bakom ert eget gränssnitt och mocka det. Mocken verifierar då ert kontrakt, medan ett integrationstest för adaptern verifierar den riktiga kopplingen.

<?php
interface PaymentGateway {            // you own this
    public function charge(int $cents, string $token): string;
}

final class StripeGateway implements PaymentGateway {
    public function __construct(private \Stripe\StripeClient $client) {}
    public function charge(int $cents, string $token): string {
        return $this->client->paymentIntents->create([/* ... */])->id;
    }
}
// Tests mock PaymentGateway, never \Stripe\StripeClient directly.

Stäng alltid och verifiera antal

Två praktiska måsten:

  • Anropa Mockery::close() i tearDown() (eller använd traiten MockeryPHPUnitIntegration) så att förväntningarna faktiskt verifieras och globala tillstånd rensas.
  • Använd uttryckliga antal (once(), times(n), never()) — vaga mockar låter buggar slinka igenom.
<?php
use PHPUnit\Framework\TestCase;
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;

final class OrderServiceTest extends TestCase
{
    use MockeryPHPUnitIntegration; // auto-calls Mockery::close()

    public function test_does_not_refund_paid_orders(): void
    {
        $gw = \Mockery::mock(PaymentGateway::class);
        $gw->shouldReceive('refund')->never();
        // ... exercise SUT ...
    }
}

Ordnade förväntningar

Ibland är anropsordningen en del av kontraktet — ni måste anropa beginTransaction() före commit(). Mockerys ordered() tvingar fram sekvensen och får testet att misslyckas om anropen kommer i fel ordning. Använd det bara när ordningen verkligen spelar roll; alltför detaljerade ordningskrav gör tester sköra.

<?php
use Mockery;

$tx = Mockery::mock(Transaction::class);
$tx->shouldReceive('begin')->once()->ordered();
$tx->shouldReceive('commit')->once()->ordered();

$service = new TransferService($tx);
$service->run();

Mockery::close(); // fails if commit() happened before begin()

Snabb kontroll

Stub eller mock — vilken ska användas till vad?

Sammanfattning

Ni har bemästrat Mockerys testdubbletter:

  • Stubbar (allows) för frågor, mockar (expects) för kommandon och spies för verifiering i efterhand.
  • Argumentmatchare (type, on, capture) justerar graden av strikt kontroll.
  • Retursekvenser och andReturnUsing modellerar tillståndsberoende och dynamiska samarbetspartners.
  • Partiella mockar skriver över enskilda metoder; använd dem sparsamt.
  • Mocka inte sådant ni inte äger — omslut tredjepartskod bakom ert gränssnitt.
  • Anropa alltid Mockery::close() och verifiera uttryckliga anropsantal.

Nästa steg: integrations- och funktionstestning.

Gratis att börja

Lär dig PHP med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
49
Lektioner
195

Vanliga frågor

Är lektionen ”Mockning och stubbning med Mockery” gratis?

Ja – hela texten till ”Mockning och stubbning med Mockery” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i PHP Academy, kan Ni uppgradera till CoddyKit PRO. Kursen i PHP Academy innehåller totalt 4 lektioner.

Vad lär jag mig i ”Mockning och stubbning med Mockery”?

Isolera enheter med flexibla testdubbletter. Ni övar på PHP Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig PHP Academy?

Du behöver inga förkunskaper. Utbildningen i PHP Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 2 av 4.

Hur lång tid tar lektionen ”Mockning och stubbning med Mockery”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här PHP Academy-lektionen?

Ja. Varje PHP Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Arbetsflödet för testdriven utveckling
  2. Mockning och stubbning med Mockery
  3. Integrations- och funktionstestning
  4. Mutationstestning med Infection
← Tillbaka till PHP Academy