Mockning och stubbning med Mockery
Isolera enheter med flexibla testdubbletter.
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/mockeryStub, 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()itearDown()(eller använd traitenMockeryPHPUnitIntegration) 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
andReturnUsingmodellerar 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.
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
- Arbetsflödet för testdriven utveckling
- Mockning och stubbning med Mockery
- Integrations- och funktionstestning
- Mutationstestning med Infection