PHP Unit: protected und private Methoden testen

31. Juli 2013 Softwaretest von Eric Kubenka

Für ein privates PHP-Projekt, welches ich nach dem Test-Driven-Development-Prinzip entwickle, benötigte ich gerade im Anfangsstadium der Entwicklung die Möglichkeit protected-Methoden zu testen. Wie ich das gemacht habe, erläutere ich folgend. Das Schlüsselwort ist Reflection.

Zu aller erst. Natürlich kann man nun argumentieren, dass eine public-Methode nur funktionieren kann wenn alle aufgerufenen protected-Methoden auch funktionieren und durch natürlich sichergestellt wird, dass auch die nicht öffentlichen Methoden erfolgreich funktionieren.

Doch gerade in der Anfangsphase möchte ich auf Nummer sicher gehen und mich mit Tests absichern, so dass ich in Sekundenschnelle sicherstellen kann, dass alles funktioniert und die Methoden nach dem gewünschten Verhalten arbeiten.

 

Schlüsselwort Reflection

Wie ich eben schon kurz angerissen habe, ist die Verwendung von Reflection ein wichtiger Teil dieser Lösung. Doch der Reihe nach. Zu Beginn ein Beispiel, um zu verdeutlichen, was überhaupt gemeint ist, wenn ich sage, das sich eine nicht öffentliche Methode testen möchte.

Hinweis: Ich verwende Composer als Autoloader und Dependency Manager und PHPUnit.

 

<?php

class Testobject
{
	protected function doSomething($something)
	{
		return 'do '.$something;
	}
}

class TestobjectTest extends \PHPUnit_Framework_TestCase
{
	public function testDoSomething()
	{
		$test = new Testobject;
		$this->assertEquals('do something', $test->doSomething('something'));
	}
}

Die Ausführung dieses Tests endet natürlich in einem Fehler.

PHP Fatal error:  Call to protected method Testobject::doSomething() from context 'TestobjectTest' in ../TestobjectTest.php on line 16 

Um nun aber dennoch die protected-Methode zu testen, kann man sich der Reflection bedienen.

In der Programmierung bedeutet Reflexion (englisch reflection) oder Introspektion, dass ein Programm seine eigene Struktur kennt und diese, wenn nötig, modifizieren kann. Quelle: wikipedia.de

Diese Definition in dem genannten Praxisbeispiel sieht dann wie folgt aus. Es wird also davon ausgegangen, dass während der Testausführung bekannt ist, welche Methoden und Klassen in der gesamten Applikation zur Verfügung stehen. Anschließend werden die Eigenschaften modifiziert, also abgeändert um so ein verändertes Verhalten der Klasse oder der Methode zu bewirken.

<?php

class Testobject
{
	protected function doSomething($something)
	{
		return 'do '.$something;
	}
}

class TestobjectTest extends \PHPUnit_Framework_TestCase
{
	public function testDoSomething()
	{
		$test = new Testobject;
		
		$this->assertEquals(
			'do something',
			$this->callProtectedMethod($test, 'doSomething', array('something'))
			);
	}

	public function callProtectedMethod($obj, $methodName, array $args)
	{
		$class = new \ReflectionClass($obj);
		$method = $class->getMethod($methodName);
		$method->setAccessible(true);

		return $method->invokeArgs($obj, $args);
	}
}

Durch die kleine Hilfsmethode callProtectedMethod wird folgender Ablauf ausgeführt. Zuerst wird von dem übergebenen Objekt mittels Reflection die unterliegende Klasse ermittelt. Anschließend wird die im Parameter $methodName übergebene Methode mittels der Funktion getMethod ermittelt und über setAccessible die Sichtbarkeit public gesetzt. Abschließend wird die Methode noch mit den übergebenen Parametern aufgerufen.

Dadurch kann nun im Test mittels callProtectedMethod auf die nicht öffentliche Methode von Testobject zugegriffen wären. Somit kann sichergestellt werden, dass auch diese Methode einwandfrei funktioniert. Das hat im Anfangsstadium der Entwicklung für mich eine enorme Relevanz, wenn ich nach dem TDD-Prinzip arbeite. Im späteren Verlauf können die Tests entfernt werden, man kann sie aber auch einfach drin lassen, wenn sie eh schon mal geschrieben sind ;).

Das war es auch schon. Ich hoffe, dass ich Euch damit kurz einen Lösungsweg präsentiert habe, falls ihr ähnlich denkt wie ich.

Zurück