Mocking og stubbing med Mockery
Isoler enheter med fleksible testdobler.
Mocking og stubbing med Mockery er en gratis leksjon i PHP Academy på CoddyKit. Dette er leksjon 2 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i PHP Academy, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i PHP Academy inneholder totalt 4 leksjoner.
Hvorfor et eget mockingbibliotek
PHPUnit leveres med sitt eget API for testdobler, men Mockery tilbyr en mer flytende og uttrykksfull syntaks samt funksjoner som PHPUnit-mockene mangler – delvise mocker, spioner, fleksibel argumenttilpasning og forventninger med rekkefølge. For kompleks kode med mange samarbeidspartnere er Mockery ofte langt mer lesbar. Denne leksjonen dekker hele spekteret av testdobler og når de bør brukes.
composer require --dev mockery/mockeryStub vs Mock vs Spy
Presist vokabular hindrer forvirrede tester:
- Stub – returnerer forhåndsdefinerte verdier; du kontrollerer tilstand (resultatet).
- Mock – har forventninger til kall; du kontrollerer atferd (at det ble kalt riktig). Forventninger som ikke oppfylles, feiler testen.
- Spy – registrerer kall og lar deg kontrollere dem i etterkant.
Tommelfingerregel: foretrekk stubber for spørringer og mocker/spioner for kommandoer.
En stub med allows()
Bruk Mockery::mock() og allows() (eller shouldReceive()->andReturn()) for å levere forhåndsdefinerte returverdier uten å kontrollere at kallet faktisk fant sted. Her leverer en gateway-stub en kjent vekslingskurs, slik at vi kan teste beregningen isolert.
<?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 selve samhandlingen er kontrakten – for eksempel «en e-post må sendes nøyaktig én gang» – bruker du expects() eller shouldReceive()->once(). Mockery verifiserer forventningen under Mockery::close(); hvis forventningen ikke oppfylles, feiler testen.
<?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
Argumentmatchere
Mockerys matchere lar forventningene være så løse eller strenge som nødvendig:
Mockery::any()– hvilken som helst verdi.Mockery::type('string')/ et klassenavn – typekontroll.Mockery::on(fn($a) => ...)– egendefinert predikat.Mockery::capture($var)– fang opp argumentet for senere kontroller.
<?php
use Mockery;
$repo = Mockery::mock(UserRepo::class);
$repo->expects()
->save(Mockery::on(fn(User $u) => $u->isActive()))
->once()
->andReturnTrue();
Retursekvenser og dynamiske returer
Du kan sette opp flere returverdier for påfølgende kall, eller beregne returverdien ut fra argumentene med andReturnUsing(). Dette modellerer retry-logikk, paginering eller tilstandsbærende samarbeidspartnere.
<?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));
Spioner: Kontroller i etterkant
En spy snur rekkefølgen: utfør først, og kontroller deretter. Mockery::spy() registrerer kall; etterpå spør du etter dem med shouldHaveReceived(). Spioner holder Arrange-Act-Assert-strukturen ryddig når du ikke vil at forhåndsdefinerte forventninger skal gjøre oppsettet uoversiktlig.
<?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();
Delvise mocker
Noen ganger ønsker du et ekte objekt, men med én metode overstyrt – en partial mock. Mockerys makePartial() kaller gjennom til ekte metoder, bortsett fra dem du har satt forventninger på. Bruk dette sparsomt: Omfattende bruk av delvise mocker tyder vanligvis på at en klasse gjør for mye.
<?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();
Ikke mock det du ikke eier
Et grunnleggende testprinsipp er å unngå å mocke tredjepartsklasser direkte. API-ene deres kan endres, og mocken din kan gradvis avvike fra virkeligheten uten at du merker det. Pakk dem i stedet inn bak ditt eget grensesnitt, og mock dette. Mocken verifiserer da din kontrakt, mens en integrasjonstest av adapteren verifiserer den faktiske koblingen.
<?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.
Lukk alltid, og kontroller antall kall
To praktiske ting er obligatoriske:
- Kall
Mockery::close()itearDown()(eller bruk traitetMockeryPHPUnitIntegration), slik at forventningene faktisk verifiseres og globale tilstander ryddes opp. - Bruk eksplisitte antall (
once(),times(n),never()) – upresise mocker slipper feil gjennom.
<?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 ...
}
}
Forventninger med rekkefølge
Av og til er kallerekkefølgen en del av kontrakten – du må kalle beginTransaction() før commit(). Mockerys ordered() håndhever rekkefølgen og feiler testen hvis kallene kommer i feil rekkefølge. Bruk dette bare når rekkefølgen faktisk er viktig; overdetaljert spesifisering av rekkefølge gjør testene skjøre.
<?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()
Hurtigsjekk
Stub eller mock – hva brukes til hva?
Oppsummering
Du har fått kontroll på Mockerys testdobler:
- Stubs (
allows) for spørringer, mocks (expects) for kommandoer og spioner for kontroller i etterkant. - Argumentmatchere (
type,on,capture) finjusterer strengheten. - Retursekvenser og
andReturnUsingmodellerer tilstandsfulle eller dynamiske samarbeidspartnere. - Delvise mocker overstyrer enkeltmetoder; bruk dem sparsomt.
- Ikke mock det du ikke eier – pakk tredjeparter inn bak ditt eget grensesnitt.
- Kall alltid
Mockery::close(), og kontroller eksplisitte antall kall.
Neste: integrasjons- og funksjonstesting.
Lær deg PHP med en AI-veileder – gratis
Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.
- Kurs
- 49
- Leksjoner
- 195
Ofte stilte spørsmål
Er leksjonen «Mocking og stubbing med Mockery» gratis?
Ja – hele teksten i «Mocking og stubbing med Mockery» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av PHP Academy-kurset, kan du oppgradere til CoddyKit PRO. Kurset i PHP Academy inneholder totalt 4 leksjoner.
Hva lærer jeg i «Mocking og stubbing med Mockery»?
Isoler enheter med fleksible testdobler. Du øver på PHP Academy med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.
Trenger jeg erfaring for å begynne med PHP Academy?
Ingen tidligere erfaring er nødvendig. PHP Academy på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 2 av 4.
Hvor lang tid tar leksjonen «Mocking og stubbing med Mockery»?
De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.
Kan jeg skrive og kjøre kode i denne PHP Academy-leksjonen?
Ja. Alle PHP Academy-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.
Alle leksjonene i dette kurset
- Arbeidsflyten i testdrevet utvikling
- Mocking og stubbing med Mockery
- Integrasjons- og funksjonstesting
- Mutasjonstesting med Infection