Frage

Ich fand die Diskussion über Haben Sie private Methode informativ testen.

Ich habe beschlossen, dass in einigen Klassen, ich mag geschützte Methoden haben, aber testen. Einige dieser Methoden sind statisch und kurz. Da die meisten der öffentlichen Methoden von ihnen Gebrauch machen, werde ich wahrscheinlich in der Lage sein, ohne Schwierigkeiten später die Tests zu entfernen. Aber mit einem TDD-Ansatz Starten und Debuggen vermeiden, möchte ich wirklich, sie testen.

Ich dachte an die folgenden:

  • Methode Objekt wie in eine Antwort scheint für diese Overkill zu sein.
  • Starten Sie mit öffentlichen Methoden und wenn Code-Coverage durch höhere Level Tests gegeben ist, schalten sie die Tests geschützt und entfernen.
  • Vererben eine Klasse mit einer prüfbaren Schnittstelle zu machen geschützten Methoden öffentlich

Welches ist beste Praxis? Gibt es noch etwas?

Es scheint, dass JUnit automatisch Methoden Änderungen geschützt öffentlich zu sein, aber ich habe nicht einen tieferen Blick auf sie haben. PHP nicht zulässt, dass diese über Reflexion .

War es hilfreich?

Lösung

Wenn Sie mit PHP5 (> = 5.3.2) mit PHPUnit, können Sie Ihre privaten und geschützten Methoden testen, indem Reflexion mit ihnen einzustellen öffentlich zu sein, bevor Sie Tests ausgeführt werden:

protected static function getMethod($name) {
  $class = new ReflectionClass('MyClass');
  $method = $class->getMethod($name);
  $method->setAccessible(true);
  return $method;
}

public function testFoo() {
  $foo = self::getMethod('foo');
  $obj = new MyClass();
  $foo->invokeArgs($obj, array(...));
  ...
}

Andere Tipps

Sie scheinen bewusst schon zu sein, aber ich werde es einfach mal so neu formulieren; Es ist ein schlechtes Zeichen, wenn Sie geschützte Methoden testen müssen. Das Ziel eines Unit-Test, ist die Schnittstelle einer Klasse zu testen und geschützte Methoden sind Implementierungsdetails. Das heißt, es gibt Fälle, wo es Sinn macht. Wenn Sie die Vererbung verwenden, können Sie eine übergeordnete Klasse als eine Schnittstelle für die Unterklasse sehen. Also hier, würden Sie haben die geschützte Methode testen (aber nie eine privat eins). Die Lösung hierfür ist eine Unterklasse zu Testzwecken zu erstellen, und diese Methoden aufzudecken verwenden. Zum Beispiel:.

class Foo {
  protected function stuff() {
    // secret stuff, you want to test
  }
}

class SubFoo extends Foo {
  public function exposedStuff() {
    return $this->stuff();
  }
}

Beachten Sie, dass Sie immer Vererbung mit der Zusammensetzung ersetzen kann. Wenn Code zu testen, ist es in der Regel viel einfacher mit dem Code zu tun, die dieses Muster verwendet, so sollten Sie diese Option prüfen.

teastburn hat die richtiger Ansatz. Noch einfacher ist direkt die Methode aufzurufen und die Antwort zurück:

class PHPUnitUtil
{
  public static function callMethod($obj, $name, array $args) {
        $class = new \ReflectionClass($obj);
        $method = $class->getMethod($name);
        $method->setAccessible(true);
        return $method->invokeArgs($obj, $args);
    }
}

Sie können diese rufen Sie einfach in Ihren Tests durch:

$returnVal = PHPUnitUtil::callMethod(
                $this->object,
                '_nameOfProtectedMethod', 
                array($arg1, $arg2)
             );

Ich möchte eine leichte Variation zu getMethod () in Antwort des uckelman.

Diese Version ändert getMethod () durch hartcodierte Werte zu entfernen und zu vereinfachen Nutzung ein wenig. Ich empfehle das Hinzufügen es zu PHPUnitUtil Klasse wie im Beispiel unten oder auf Ihre PHPUnit_Framework_TestCase erstreckender Klasse (oder, nehme ich an, global zu Ihrer PHPUnitUtil-Datei).

Da MyClass instanziiert wird sowieso und Reflection kann einen String oder ein Objekt nehmen ...

class PHPUnitUtil {
    /**
     * Get a private or protected method for testing/documentation purposes.
     * How to use for MyClass->foo():
     *      $cls = new MyClass();
     *      $foo = PHPUnitUtil::getPrivateMethod($cls, 'foo');
     *      $foo->invoke($cls, $...);
     * @param object $obj The instantiated instance of your class
     * @param string $name The name of your private/protected method
     * @return ReflectionMethod The method you asked for
     */
    public static function getPrivateMethod($obj, $name) {
      $class = new ReflectionClass($obj);
      $method = $class->getMethod($name);
      $method->setAccessible(true);
      return $method;
    }
    // ... some other functions
}

Ich habe auch eine Alias-Funktion getProtectedMethod () explizit zu sein, was zu erwarten ist, sondern dass man die bis zu Ihnen.

Cheers!

Ich denke, troelskn nahe ist. Ich würde dies tun, anstatt:

class ClassToTest
{
   protected testThisMethod()
   {
     // Implement stuff here
   }
}

Dann implementieren etwas wie folgt aus:

class TestClassToTest extends ClassToTest
{
  public testThisMethod()
  {
    return parent::testThisMethod();
  }
}

Sie führen Sie Ihre Tests gegen TestClassToTest.

Es sollte möglich sein, automatisch eine solche Verlängerung Klassen zu erzeugen, indem sie den Code Parsen. Ich wäre nicht überrascht, wenn PHPUnit bereits einen solchen Mechanismus bietet (obwohl ich nicht überprüft haben).

Ich werde meinen Hut in den Ring werfen hier:

Ich habe die __call zu hacken mit gemischten Erfolgsgraden verwendet. Die Alternative, die ich herauskommen, war das Besuchermuster zu verwenden:

1: Erzeugen eines stdClass oder benutzerdefinierte Klasse (erzwingen Typ)

2: prime dass mit den erforderlichen Verfahren und Argumenten

3: Stellen Sie sicher, dass Ihr SUT eine acceptVisitor Methode hat, die das Verfahren mit den Argumenten in der Gast Klasse angegeben ausgeführt wird

4: injizieren sie in die Klasse, die Sie testen möchten

5: SUT spritzt das Ergebnis der Operation in die Besucher

6: anwenden, um Ihre Testbedingungen auf das Ergebnis Attribut des Besuchers

Sie können in die Tat verwenden __call () in einer generischen Art und Weise geschützte Methoden zuzugreifen. Um diese Klasse zu testen

class Example {
    protected function getMessage() {
        return 'hello';
    }
}

Sie erstellen eine Unterklasse in ExampleTest.php:

class ExampleExposed extends Example {
    public function __call($method, array $args = array()) {
        if (!method_exists($this, $method))
            throw new BadMethodCallException("method '$method' does not exist");
        return call_user_func_array(array($this, $method), $args);
    }
}

Beachten Sie, dass die __call () -Methode nicht die Klasse in irgendeiner Weise referenziert, so dass Sie die oben für jede Klasse mit geschützten Methoden kopieren können Sie testen möchten, und nur die Klassendeklaration ändern. Sie können diese Funktion in einer gemeinsamen Basisklasse platzieren können, aber ich habe es nicht versucht.

Jetzt ist der Testfall selbst unterscheidet sich nur in dem Sie das Objekt konstruieren getestet werden, in ExampleExposed für Beispiel Swapping.

class ExampleTest extends PHPUnit_Framework_TestCase {
    function testGetMessage() {
        $fixture = new ExampleExposed();
        self::assertEquals('hello', $fixture->getMessage());
    }
}

Ich glaube, PHP 5.3 ermöglicht es Ihnen, Reflexion zu verwenden, um die Zugänglichkeit von Methoden direkt zu ändern, aber ich nehme an, Sie würden so für jedes Verfahren zu tun haben.

Ich schlage vor, folgende Abhilfe für "Henrik Paul" 's Abhilfe / Idee:)

Sie wissen, Namen von privaten Methoden der Klasse. Zum Beispiel sind sie wie _add (), _edit (), _delete () etc.

So, wenn Sie es aus Aspekt der Komponententests testen möchten, rufen Sie einfach private Methoden durch Voranstellen und / oder suffixing einige gemeinsam Wort (zum Beispiel _addPhpunit), so dass, wenn __call () -Methode ist genannt (da Methode _addPhpunit () ist nicht vorhanden) die Eigentümer Klasse, die Sie gerade notwendigen Code in __call put () Methode voran / nachgestelltes Wort / s (PHPUnit) zu entfernen und dann von dort aus, dass die abgeleitete privaten Methode zu nennen. Dies ist eine weitere gute Verwendung von magischen Methoden.

Probieren Sie es aus.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top