0Pricing
PHP Academy · Leçon

Simulations et bouchons avec Mockery

Isolez les unités grâce à des doublures de test flexibles.

Simulations et bouchons avec Mockery est une leçon PHP Academy gratuite sur CoddyKit. Ceci est la leçon 2 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage PHP Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours PHP Academy comprend 4 leçons au total.

Pourquoi une bibliothèque dédiée aux doublures

PHPUnit fournit sa propre API de doublures de test, mais Mockery offre une syntaxe plus fluide et expressive ainsi que des fonctionnalités absentes des doublures de PHPUnit — doublures partielles, espions, correspondance d'arguments flexible et attentes ordonnées. Pour le code riche en collaborations complexes, Mockery est souvent bien plus lisible. Cette leçon couvre tout l'éventail des doublures et le moment où utiliser chacune.

composer require --dev mockery/mockery

Bouchon, simulacre ou espion

Un vocabulaire précis évite les tests confus :

  • Bouchon — renvoie des valeurs prédéfinies ; vous vérifiez l'état (le résultat).
  • Simulacre — comporte des attentes concernant les appels ; vous vérifiez le comportement (l'appel a été effectué correctement). Des attentes non satisfaites font échouer le test.
  • Espion — enregistre les appels et vous permet de les vérifier après coup.

Règle générale : préférez les bouchons pour les requêtes, et les simulacres ou les espions pour les commandes.

Un bouchon avec allows()

Utilisez Mockery::mock() et allows() (ou shouldReceive()->andReturn()) pour fournir des valeurs de retour prédéfinies sans vérifier que l'appel a eu lieu. Ici, un bouchon de passerelle fournit un taux de change connu afin que nous puissions tester le calcul isolément.

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

Un simulacre avec expects()

Lorsque l'interaction elle-même constitue le contrat — par exemple, un courriel doit être envoyé exactement une fois — utilisez expects() ou shouldReceive()->once(). Mockery vérifie l'attente lors de Mockery::close() ; un appel non satisfait fait échouer le test.

<?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

Correspondances d'arguments

Les correspondances de Mockery permettent de rendre les attentes aussi souples ou strictes que nécessaire :

  • Mockery::any() — toute valeur.
  • Mockery::type('string') / un nom de classe — vérification du type.
  • Mockery::on(fn($a) => ...) — prédicat personnalisé.
  • Mockery::capture($var) — capture de l'argument pour des vérifications ultérieures.
<?php
use Mockery;

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

Séquences de retours et retours dynamiques

Vous pouvez définir plusieurs valeurs de retour pour des appels successifs ou calculer le retour à partir des arguments avec andReturnUsing(). Cela permet de modéliser une logique de nouvelle tentative, la pagination ou des collaborateurs avec état.

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

Espions : vérifier après coup

Un espion inverse l'ordre : agissez d'abord, puis vérifiez. Mockery::spy() enregistre les appels ; ensuite, vous les interrogez avec shouldHaveReceived(). Les espions préservent la structure Organiser-Agir-Vérifier lorsque vous ne voulez pas que des attentes préconfigurées encombrent la configuration.

<?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();

Simulacres partiels

Parfois, vous voulez un objet réel dont une seule méthode est remplacée : un simulacre partiel. Le makePartial() de Mockery appelle les méthodes réelles, sauf celles pour lesquelles vous définissez des attentes. Utilisez-les avec modération : une forte dépendance aux simulacres partiels signale généralement qu'une classe en fait trop.

<?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();

Ne simulez pas ce qui ne vous appartient pas

Un principe fondamental des tests : évitez de simuler directement les classes tierces. Leurs API peuvent changer et votre simulacre peut s'écarter silencieusement de la réalité. À la place, encapsulez-les derrière votre propre interface et simulez cette interface. Le simulacre vérifie alors votre contrat, et un test d'intégration de l'adaptateur vérifie la liaison réelle.

<?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.

Fermez toujours et vérifiez les nombres d'appels

Deux obligations opérationnelles :

  • Appelez Mockery::close() dans tearDown() (ou utilisez le trait MockeryPHPUnitIntegration) afin que les attentes soient effectivement vérifiées et que les variables globales soient nettoyées.
  • Utilisez des nombres explicites (once(), times(n), never()) : les simulacres vagues laissent passer des bogues.
<?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 ...
    }
}

Attentes ordonnées

Il arrive que l'ordre des appels fasse partie du contrat : vous devez appeler beginTransaction() avant commit(). Le ordered() de Mockery impose la séquence et fait échouer le test si les appels arrivent dans le mauvais ordre. Utilisez-le uniquement lorsque l'ordre compte réellement ; une surspécification de l'ordre produit des tests fragiles.

<?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()

Vérification rapide

Bouchon ou simulacre : lequel utiliser et quand ?

Récapitulatif

Vous maîtrisez les doublures de Mockery :

  • Les bouchons (allows) pour les requêtes, les simulacres (expects) pour les commandes et les espions pour les vérifications après coup.
  • Les correspondances d'arguments (type, on, capture) permettent d'ajuster le degré de stricte vérification.
  • Les séquences de retours et andReturnUsing modélisent les collaborateurs avec état ou dynamiques.
  • Les simulacres partiels remplacent des méthodes uniques ; utilisez-les avec modération.
  • Ne simulez pas ce qui ne vous appartient pas : encapsulez les bibliothèques tierces derrière votre interface.
  • Appelez toujours Mockery::close() et vérifiez explicitement le nombre d'appels.

Ensuite : tests d'intégration et tests fonctionnels.

Questions Fréquemment Posées

La leçon « Simulations et bouchons avec Mockery » est-elle gratuite ?

Oui — le texte complet de « Simulations et bouchons avec Mockery » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours PHP Academy, passe à CoddyKit PRO. Le cours PHP Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Simulations et bouchons avec Mockery » ?

Isolez les unités grâce à des doublures de test flexibles. Tu pratiques PHP Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer PHP Academy ?

Aucune expérience préalable n'est requise. PHP Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 2 sur 4.

Combien de temps prend la leçon « Simulations et bouchons avec Mockery » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon PHP Academy ?

Oui. Chaque leçon PHP Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Le flux de développement piloté par les tests
  2. Simulations et bouchons avec Mockery
  3. Tests d’intégration et tests fonctionnels
  4. Tests de mutation avec Infection
← Retour à PHP Academy