Heim  >  Artikel  >  Backend-Entwicklung  >  Scheinobjekte beim PHPUnit-Testen verstehen

Scheinobjekte beim PHPUnit-Testen verstehen

DDD
DDDOriginal
2024-09-22 16:17:02543Durchsuche

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 Folgendes:

  • 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 in den folgenden Szenarien besonders nützlich:

  • 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 überflüssig machen.
  • 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?

Wenn Sie mit Scheinobjekten arbeiten, stoßen Sie auf zwei Begriffe: 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);
    }
}

Erklärung:

  • 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) creates a mock object simulating the PaymentGateway interface.
  • method('charge')->with(100.0)->willReturn(true) specifies that when the charge method is called with 100.0 as an argument, it should return true.
  • The mock object is passed to the PaymentProcessor class, allowing you to test processPayment without relying on a real payment gateway.

Verifying Interactions

You can also verify that the charge method is called exactly once when processing a payment:

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 this example, expects($this->once()) ensures that the charge method is called exactly once. If the method is not called, or called more than once, the test will fail.

Example: Testing with a Repository

Let’s assume you have a UserService class that depends on a UserRepository to fetch user data. To test UserService in isolation, you can mock the UserRepository.

class UserService
{
    private $repository;

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

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

To test this class, we can mock the repository:

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 for Using Mocks

  1. Use Mocks Only When Necessary: Mocks are useful for isolating code, but overuse can make tests hard to understand. Only mock dependencies that are necessary for the test.
  2. Focus on Behavior, Not Implementation: Mocks should help test the behavior of your code, not the specific implementation details of dependencies.
  3. Avoid Mocking Too Many Dependencies: If a class requires many mocked dependencies, it might be a sign that the class has too many responsibilities. Refactor if needed.
  4. Verify Interactions Sparingly: Avoid over-verifying method calls unless essential to the test.

Conclusion

Mock objects are invaluable tools for writing unit tests in PHPUnit. They allow you to isolate your code from external dependencies, ensuring that your tests are faster, more reliable, and easier to maintain. Mock objects also help verify interactions between the code under test and its dependencies, ensuring that your code behaves correctly in various scenarios

Das obige ist der detaillierte Inhalt vonScheinobjekte beim PHPUnit-Testen verstehen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Vorheriger Artikel:Was ist der PHP CodeSniffer?Nächster Artikel:Was ist der PHP CodeSniffer?