Testen abstrakter Klassen

144

Wie teste ich die konkreten Methoden einer abstrakten Klasse mit PHPUnit?

Ich würde erwarten, dass ich im Rahmen des Tests eine Art Objekt erstellen muss. Ich habe jedoch keine Ahnung, wie dies am besten funktioniert oder ob PHPUnit dies zulässt.

Mez
quelle
10
Vielleicht sollten Sie die akzeptierte Antwort ändern.
Jacob
1
Vielleicht hilft stackoverflow.com/a/2947823/23963 .
Nigel Thorne

Antworten:

240

Das Testen von abstrakten Klassen in Einheiten bedeutet nicht unbedingt das Testen der Schnittstelle, da abstrakte Klassen konkrete Methoden haben können und diese konkreten Methoden getestet werden können.

Es ist nicht so ungewöhnlich, dass beim Schreiben von Bibliothekscode bestimmte Basisklassen in Ihrer Anwendungsschicht erweitert werden. Und wenn Sie sicherstellen möchten, dass der Bibliothekscode getestet wird, benötigen Sie Mittel, um die konkreten Methoden abstrakter Klassen zu verwenden.

Persönlich verwende ich PHPUnit und es hat sogenannte Stubs und Scheinobjekte, um Ihnen beim Testen dieser Art von Dingen zu helfen.

Direkt aus dem PHPUnit-Handbuch :

abstract class AbstractClass
{
    public function concreteMethod()
    {
        return $this->abstractMethod();
    }

    public abstract function abstractMethod();
}

class AbstractClassTest extends PHPUnit_Framework_TestCase
{
    public function testConcreteMethod()
    {
        $stub = $this->getMockForAbstractClass('AbstractClass');
        $stub->expects($this->any())
             ->method('abstractMethod')
             ->will($this->returnValue(TRUE));

        $this->assertTrue($stub->concreteMethod());
    }
}

Mock-Objekt gibt Ihnen mehrere Dinge:

  • Sie müssen keine konkrete Implementierung der abstrakten Klasse haben und können stattdessen mit stub davonkommen
  • Sie können konkrete Methoden aufrufen und behaupten, dass sie korrekt funktionieren
  • Wenn sich die konkrete Methode auf eine nicht implementierte (abstrakte) Methode stützt, können Sie den Rückgabewert mit der will () PHPUnit-Methode stubben
Victor Farazdagi
quelle
38

Das ist eine gute Frage. Ich habe auch danach gesucht.
Glücklicherweise hat PHPUnit bereits eine getMockForAbstractClass()Methode für diesen Fall, z

protected function setUp()
{
    $stub = $this->getMockForAbstractClass('Some_Abstract_Class');
    $this->_object = $stub;
}

Wichtig:

Beachten Sie, dass hierfür PHPUnit> 3.5.4 erforderlich ist. In früheren Versionen gab es einen Fehler .

So aktualisieren Sie auf die neueste Version:

sudo pear channel-update pear.phpunit.de
sudo pear upgrade phpunit/PHPUnit
Takeshin
quelle
Klingt interessant, aber Sie würden gegen den Schein testen? Wie würden die Tests aussehen? IE: Den Mock im Testfall erweitern und gegen die erweiterte Testklasse testen?
Stefgosselin
34

Es ist zu beachten, dass ab PHP 7 die Unterstützung für anonyme Klassen hinzugefügt wurde. Dies gibt Ihnen eine zusätzliche Möglichkeit, einen Test für eine abstrakte Klasse einzurichten, der nicht von PHPUnit-spezifischen Funktionen abhängt.

class AbstractClassTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @var AbstractClass
     */
    private $testedClass;

    public function setUp()
    {
        $this->testedClass = new class extends AbstractClass {

            protected function abstractMethod()
            {
                // Put a barebones implementation here
            }
        };
    }

    // Put your tests here
}
GordonM
quelle
4
Danke dafür! Die Verwendung einer anonymen Klasse in PHPUnit gab mir viel Flexibilität bei der Erstellung meiner verschiedenen Tests.
Alice Wonder
1

Eran, Ihre Methode sollte funktionieren, aber sie widerspricht der Tendenz, den Test vor dem eigentlichen Code zu schreiben.

Ich würde vorschlagen, Ihre Tests auf die gewünschte Funktionalität einer nicht abstrakten Unterklasse der betreffenden abstrakten Klasse zu schreiben, dann sowohl die abstrakte Klasse als auch die implementierende Unterklasse zu schreiben und schließlich den Test auszuführen.

Ihre Tests sollten natürlich die definierten Methoden der abstrakten Klasse testen, jedoch immer über die Unterklasse.


quelle
Ich finde als willkürliche Antwort: Sie haben eine abstrakte Klasse 'A' mit einer gemeinsamen 'foo ()' Methode. Diese 'foo ()' - Methode wird insgesamt für alle 'B'- und' C'-Klassen verwendet, die beide von 'A' abgeleitet sind. Welche Klasse würdest du wählen, um 'foo ()' zu testen?
user3790897
1

Nelsons Antwort ist falsch.

Abstrakte Klassen erfordern nicht, dass alle Methoden abstrakt sind.

Die implementierten Methoden müssen getestet werden.

Sie können eine gefälschte Stub-Klasse in der Unit-Test-Datei erstellen, die abstrakte Klasse erweitern und natürlich nur das implementieren, was ohne Funktionalität erforderlich ist, und dies testen.

Prost.

skqr
quelle
0

Wenn Sie die abstrakte Klasse nicht nur unterklassifizieren möchten, um einen Komponententest für die Methoden durchzuführen, die bereits in der abstrakten Klasse implementiert sind, können Sie versuchen, festzustellen, ob Ihr Framework es Ihnen ermöglicht, abstrakte Klassen zu verspotten .

Hangy
quelle