„Wenn ein Arbeiter seine Arbeit gut machen will, muss er zuerst seine Werkzeuge schärfen.“ – Konfuzius, „Die Gespräche des Konfuzius. Lu Linggong“
Titelseite > Programmierung > Scheinobjekte beim PHPUnit-Testen verstehen

Scheinobjekte beim PHPUnit-Testen verstehen

Veröffentlicht am 03.11.2024
Durchsuche:805

Understanding Mock Objects in PHPUnit Testing

Beim Schreiben von Unit-Tests besteht eine zentrale Herausforderung darin, sicherzustellen, dass sich Ihre Tests auf den zu testenden Code konzentrieren, ohne Störungen durch externe Systeme oder Abhängigkeiten. Hier kommen Mock-Objekte in PHPUnit ins Spiel. Sie ermöglichen es Ihnen, das Verhalten realer Objekte kontrolliert zu simulieren, wodurch Ihre Tests zuverlässiger und einfacher zu warten sind. In diesem Artikel untersuchen wir, was Scheinobjekte sind, warum sie nützlich sind und wie man sie effektiv in PHPUnit verwendet.

Was sind Scheinobjekte?

Mock-Objekte sind simulierte Versionen realer Objekte, die in Unit-Tests verwendet werden. Sie ermöglichen Ihnen:

  • Isolieren Sie den zu testenden Code: Scheinobjekte simulieren das Verhalten von Abhängigkeiten und stellen so sicher, dass die Testergebnisse von der tatsächlichen Implementierung dieser Abhängigkeiten nicht beeinflusst werden.
  • Abhängigkeitsverhalten steuern: Sie können angeben, wie sich der Mock verhalten soll, wenn bestimmte Methoden aufgerufen werden, sodass Sie verschiedene Szenarien testen können.
  • Interaktionen überprüfen: Mocks verfolgen Methodenaufrufe und ihre Parameter und stellen so sicher, dass der zu testende Code korrekt mit seinen Abhängigkeiten interagiert.

Warum Scheinobjekte verwenden?

Mocks sind besonders nützlich in den folgenden Szenarien:

  • Komplexe Abhängigkeiten: Wenn Ihr Code auf externen Systemen wie Datenbanken, APIs oder Drittanbieterdiensten basiert, vereinfachen Scheinobjekte das Testen, indem sie die Notwendigkeit einer Interaktion mit diesen Systemen beseitigen.
  • Interaktionstests: Mit Mocks können Sie überprüfen, ob bestimmte Methoden mit den richtigen Argumenten aufgerufen werden, um sicherzustellen, dass sich Ihr Code wie erwartet verhält.
  • Schnellere Testausführung: Reale Vorgänge wie Datenbankabfragen oder API-Anfragen können Tests verlangsamen. Das Verspotten dieser Abhängigkeiten sorgt für eine schnellere Testausführung.

Stubbing vs. Mocking: Was ist der Unterschied?

Bei der Arbeit mit Scheinobjekten werden Sie auf zwei Begriffe stoßen: stubbing und mocking:

  • Stubbing: Bezieht sich auf die Definition des Verhaltens von Methoden auf einem Scheinobjekt, z. B. das Anweisen einer Methode, einen bestimmten Wert zurückzugeben.
  • Verspottung: Beinhaltet das Festlegen von Erwartungen darüber, wie Methoden aufgerufen werden sollen, z. B. die Überprüfung der Anzahl der Methodenaufrufe und ihrer Parameter.

So erstellen und verwenden Sie Scheinobjekte in PHPUnit

PHPUnit erleichtert das Erstellen und Verwenden von Scheinobjekten mit der Methode createMock(). Nachfolgend finden Sie einige Beispiele, die zeigen, wie Sie effektiv mit Scheinobjekten arbeiten.

Beispiel 1: Grundlegende Verwendung von Scheinobjekten

In diesem Beispiel erstellen wir ein Scheinobjekt für eine Klassenabhängigkeit und geben dessen Verhalten an.

use PHPUnit\Framework\TestCase;

class MyTest extends TestCase
{
    public function testMockExample()
    {
        // Create a mock for the SomeClass dependency
        $mock = $this->createMock(SomeClass::class);

        // Specify that when the someMethod method is called, it returns 'mocked value'
        $mock->method('someMethod')
             ->willReturn('mocked value');

        // Pass the mock object to the class under test
        $unitUnderTest = new ClassUnderTest($mock);

        // Perform the action and assert that the result matches the expected value
        $result = $unitUnderTest->performAction();
        $this->assertEquals('expected result', $result);
    }
}

Erläuterung:

  • createMock(SomeClass::class) erstellt ein Scheinobjekt für SomeClass.
  • method('someMethod')->willReturn('mocked value') definiert das Verhalten des Mocks.
  • Das Scheinobjekt wird an die getestete Klasse übergeben, um sicherzustellen, dass die echte SomeClass-Implementierung nicht verwendet wird.

Beispiel 2: Methodenaufrufe überprüfen

Manchmal müssen Sie überprüfen, ob eine Methode mit den richtigen Parametern aufgerufen wird. So können Sie das tun:

public function testMethodCallVerification()
{
    // Create a mock object
    $mock = $this->createMock(SomeClass::class);

    // Expect the someMethod to be called once with 'expected argument'
    $mock->expects($this->once())
         ->method('someMethod')
         ->with($this->equalTo('expected argument'))
         ->willReturn('mocked value');

    // Pass the mock to the class under test
    $unitUnderTest = new ClassUnderTest($mock);

    // Perform an action that calls the mock's method
    $unitUnderTest->performAction();
}

Wichtige Punkte:

  • erwartet($this->once()) stellt sicher, dass someMethod genau einmal aufgerufen wird.
  • with($this->equalTo('expected argument')) überprüft, ob die Methode mit dem richtigen Argument aufgerufen wird.

Beispiel: Testen mit PaymentProcessor

Um die reale Anwendung von Scheinobjekten zu demonstrieren, nehmen wir das Beispiel einer PaymentProcessor-Klasse, die von einer externen PaymentGateway-Schnittstelle abhängt. Wir möchten die Methode „processPayment“ von PaymentProcessor testen, ohne uns auf die tatsächliche Implementierung des PaymentGateway zu verlassen.

Hier ist die PaymentProcessor-Klasse:

class PaymentProcessor
{
    private $gateway;

    public function __construct(PaymentGateway $gateway)
    {
        $this->gateway = $gateway;
    }

    public function processPayment(float $amount): bool
    {
        return $this->gateway->charge($amount);
    }
}

Jetzt können wir ein Modell für das PaymentGateway erstellen, um die Methode „processPayment“ zu testen, ohne mit dem eigentlichen Zahlungsgateway zu interagieren.

Testen des PaymentProcessor mit Mock Objects

use PHPUnit\Framework\TestCase;

class PaymentProcessorTest extends TestCase
{
    public function testProcessPayment()
    {
        // Create a mock object for the PaymentGateway interface
        $gatewayMock = $this->createMock(PaymentGateway::class);

        // Define the expected behavior of the mock
        $gatewayMock->method('charge')
                    ->with(100.0)
                    ->willReturn(true);

        // Inject the mock into the PaymentProcessor
        $paymentProcessor = new PaymentProcessor($gatewayMock);

        // Assert that processPayment returns true
        $this->assertTrue($paymentProcessor->processPayment(100.0));
    }
}

Aufschlüsselung des Tests:

  • createMock(PaymentGateway::class) erstellt ein Scheinobjekt, das die PaymentGateway-Schnittstelle simuliert.
  • method('charge')->with(100.0)->willReturn(true) gibt an, dass beim Aufruf der Charge-Methode mit 100.0 als Argument true zurückgegeben werden soll.
  • Das Scheinobjekt wird an die PaymentProcessor-Klasse übergeben, sodass Sie ProcessPayment testen können, ohne auf ein echtes Zahlungsgateway angewiesen zu sein.

Interaktionen überprüfen

Sie können auch überprüfen, ob die Abbuchungsmethode bei der Verarbeitung einer Zahlung genau einmal aufgerufen wird:

public function testProcessPaymentCallsCharge()
{
    $gatewayMock = $this->createMock(PaymentGateway::class);

    // Expect the charge method to be called once with the argument 100.0
    $gatewayMock->expects($this->once())
                ->method('charge')
                ->with(100.0)
                ->willReturn(true);

    $paymentProcessor = new PaymentProcessor($gatewayMock);
    $paymentProcessor->processPayment(100.0);
}

In diesem Beispiel stellt „expected($this->once())“ sicher, dass die Charge-Methode genau einmal aufgerufen wird. Wenn die Methode nicht oder mehr als einmal aufgerufen wird, schlägt der Test fehl.

Beispiel: Testen mit einem Repository

Angenommen, Sie haben eine UserService-Klasse, die zum Abrufen von Benutzerdaten auf ein UserRepository angewiesen ist. Um UserService isoliert zu testen, können Sie das UserRepository verspotten.

class UserService
{
    private $repository;

    public function __construct(UserRepository $repository)
    {
        $this->repository = $repository;
    }

    public function getUserName($id)
    {
        $user = $this->repository->find($id);
        return $user->name;
    }
}

Um diese Klasse zu testen, können wir das Repository verspotten:

use PHPUnit\Framework\TestCase;

class UserServiceTest extends TestCase
{
    public function testGetUserName()
    {
        // Create a mock for the UserRepository
        $mockRepo = $this->createMock(UserRepository::class);

        // Define that the find method should return a user object with a predefined name
        $mockRepo->method('find')
                 ->willReturn((object) ['name' => 'John Doe']);

        // Instantiate the UserService with the mock repository
        $service = new UserService($mockRepo);

        // Assert that the getUserName method returns 'John Doe'
        $this->assertEquals('John Doe', $service->getUserName(1));
    }
}

Best Practices für die Verwendung von Mocks

  1. Mocks nur bei Bedarf verwenden: Mocks sind nützlich, um Code zu isolieren, aber übermäßiger Gebrauch kann dazu führen, dass Tests schwer verständlich sind. Nur Scheinabhängigkeiten, die für den Test notwendig sind.
  2. Konzentrieren Sie sich auf das Verhalten, nicht auf die Implementierung: Mocks sollten dabei helfen, das Verhalten Ihres Codes zu testen, nicht die spezifischen Implementierungsdetails von Abhängigkeiten.
  3. Vermeiden Sie das Verspotten zu vieler Abhängigkeiten: Wenn eine Klasse viele simulierte Abhängigkeiten erfordert, könnte dies ein Zeichen dafür sein, dass die Klasse zu viele Verantwortlichkeiten hat. Bei Bedarf umgestalten.
  4. Interaktionen sparsam überprüfen: Vermeiden Sie eine übermäßige Überprüfung von Methodenaufrufen, es sei denn, dies ist für den Test unbedingt erforderlich.

Abschluss

Mock-Objekte sind unschätzbare Werkzeuge zum Schreiben von Unit-Tests in PHPUnit. Sie ermöglichen es Ihnen, Ihren Code von externen Abhängigkeiten zu isolieren und so sicherzustellen, dass Ihre Tests schneller, zuverlässiger und einfacher zu warten sind. Scheinobjekte helfen auch dabei, Interaktionen zwischen dem zu testenden Code und seinen Abhängigkeiten zu überprüfen und sicherzustellen, dass sich Ihr Code in verschiedenen Szenarien korrekt verhält

Freigabeerklärung Dieser Artikel ist abgedruckt unter: https://dev.to/ialaminpro/understanding-mock-objects-in-phpunit-testing-c55?1 Bei Verstößen wenden Sie sich bitte an [email protected], um ihn zu löschen
Neuestes Tutorial Mehr>

Haftungsausschluss: Alle bereitgestellten Ressourcen stammen teilweise aus dem Internet. Wenn eine Verletzung Ihres Urheberrechts oder anderer Rechte und Interessen vorliegt, erläutern Sie bitte die detaillierten Gründe und legen Sie einen Nachweis des Urheberrechts oder Ihrer Rechte und Interessen vor und senden Sie ihn dann an die E-Mail-Adresse: [email protected] Wir werden die Angelegenheit so schnell wie möglich für Sie erledigen.

Copyright© 2022 湘ICP备2022001581号-3