PHPUnit: Die Reihenfolge ist entscheidend

Geschrieben von Dejan Spasic • Wednesday, 22. December 2010 • Kategorie: PHPKommentare (0)

Ich habe es schon ziemlich oft beobachtet, dass vor allem PHPUnit-Neulinge immer den selben Fehler mit der Reihenfolge der Argumenten für die assert-Methoden machen. Dabei ist die Reihenfolge der Argumenten der assert-Methoden entscheidend.

"PHPUnit: Die Reihenfolge ist entscheidend" vollständig lesen

Tags für diesen Artikel: ,

PHP 5.3.x unter Ubuntu installieren

Geschrieben von Dejan Spasic • Thursday, 18. November 2010 • Kategorie: Betriebssystem , PHPKommentare (0)

PHP steht, meines Wissens, bei jeder Unix*-Distribution zur Verfügung. Manchmal möchte man jedoch aus unterschiedlichen Beweggründen die aktuellste Version einsetzten und hier muss man meistens den manuellen Weg einschlagen. Sprich selbst Kompilieren. Ich will hier nicht zu sehr ins Detail gehen, sondern kurz und knapp aufzeigen wie man PHP unter Ubuntu bzw. Debian-Derivaten installiert.

Ich gehe zunächst davon aus das Apache 2.2.x als Web-Server ohne Thread-Unterstützung (prefork) eingesetzt wird. Falls der Webserver nicht selbst kompiliert sondern mit den Paket-Manager der Distribution installiert wurde, sollte man, falls noch nicht geschehen, noch das Packet apache2-prefork-dev nachinstallieren.

PHP möchte ich mit folgendem Modulen installieren:

[PHP Modules]
bcmath
bz2
Core
ctype
curl
date
dom
ereg
fileinfo
filter
gd
hash
iconv
intl
json
libxml
mbstring
mcrypt
mysql
mysqli
mysqlnd
openssl
pcre
PDO
pdo_mysql
pdo_sqlite
Phar
posix
Reflection
session
SimpleXML
soap
SPL
SQLite
sqlite3
standard
tokenizer
xdebug
xml
xmlreader
xmlwriter
xsl

Für diese Module benötigt PHP bzw. benötigen die jeweligen Extensions noch die entsprechenden Header-Dateien. Diese installiere ich in diesem Fall über den Paket-Manager.

apt-get install libxml2-dev libssl-dev libbz2-dev libcurl4-gnutls-dev libjpeg8-dev libpng12-dev libfreetype6-dev libt1-dev libicu-dev gcc g++ libmcrypt-dev libxslt1-dev

Nun laden wir uns PHP von der offizielle Homepage runter und entpacken die Datei. Bevor wir jetzt anfangen PHP zu installieren, müssen wir erstmal den Pfad zu apxs mit dem Befehl whereis ermitteln. In meinem Fall lautet der Pfad /usr/local/apache2/bin/apxs. Nach dem wir nun den Pfad zu apxs haben, können wir auch mit der Installation beginnen. Wir wechseln in das entpackte PHP Verzeichnis und führen folgendes Kommando aus:

./configure \
--with-apxs2=/usr/local/apache2/bin/apxs \
--enable-soap \
--with-pear \
--with-xsl \
--with-curl \
--disable-short-tags \
--with-pcre-regex \
--with-openssl \
--with-zlib \
--enable-bcmath \
--with-bz2 \
--with-gd \
--with-jpeg-dir \
--with-png-dir \
--with-freetype-dir \
--with-t1lib \
--with-xpm-dir \
--enable-gd-native-ttf \
--enable-gd-jis-conv \
--enable-intl \
--enable-mbstring \
--with-mcrypt \
--with-mysql=mysqlnd \
--with-mysqli=mysqlnd \
--with-pdo-mysql=mysqlnd \
--enable-sqlite-utf8 \
--with-iconv-dir \
--enable-zip 

Ist configure durchgelaufen, führt man noch die Befehle make und make install aus. Das sollte es auch gewesen sein. Zumindest mit der Installation.

Tags für diesen Artikel: , ,

Observer Pattern mit PHPUnit testen

Geschrieben von Dejan Spasic • Tuesday, 5. May 2009 • Kategorie: PHPKommentar (1)
Der Observer (Beobachter, Listener) ist ein Entwurfsmuster aus dem Bereich der Softwareentwicklung und gehört zu der Kategorie der Verhaltensmuster (Behavioural Patterns). Es dient zur Weitergabe von Änderungen an einem Objekt an von diesem Objekt abhängige Strukturen. Das Muster ist eines der sogenannten GoF-Muster (Gang of Four).
Quelle: Wikipedia

Die Klassen

Da SPL schon für das Entwurfmuster Schnittstellen (SplSubject, SplObserver) bereit stellt, werden wir auch diese für dieses Beispiel einsetzen.

Klasse Observer

<?php
/**
 * A Observer
 * 
 * @category   UnitTests
 * @package    Ddown
 * @subpackage DesignPatterns
 * @version    Number one
 */
class Observer implements SplObserver
{
	/**
	 * @param SplSubject $subject
	 * @return string
	 */
	public function update(SplSubject $subject)
	{
		return $subject->getValue();
	} 
} 

Klasse Subject

<?php
/**
 * The subject
 *
 * @category   UnitTests
 * @package    Ddown
 * @subpackage DesignPatterns
 * @version    Number one
 */
class Subject implements SplSubject
{
	/**
	 * @var SplObserver
	 */  
	private $observers = array();
 
	/**
	 * @var string
	 */	
	private $value = null;
 
	/**
	 * Attach a observer
	 * @param SplObserver $observer
	 * @return void
	 */
	public function attach(SplObserver $observer)
	{
		//wir fuegen hier den observer hinzu
		// um zu verhindern das der gleiche $observer hinzugefuegt
		// werden kann, verwenden wir einen eindeutigen hash des 
		// uebergebenden observers
		$this->observers[spl_object_hash($observer)] = $observer;
	}
 
	/**
	 * Detach a observer
	 * @param SplObserver $observer
	 * @return void
	 */
	public function detach(SplObserver $observer)
	{
		unset($this->observers[spl_object_hash($observer)]);
	}
 
	/**
	 * Notify all attachted observer
	 * @param SplObserver $observer
	 * @return void
	 */
	public function notify()
	{
		foreach ($this->observers as $observer)
		{
			$observer->update($this);
		}
	} 
 
	/**
	 * Setter for sValue attribute
	 *
	 * Additionaly it notifies the attachted observers
	 * @param string $value
	 * @return void
	 */
	public function setValue($value)
	{
		$this->value = $value;
		$this->notify();
	}
 
	/**
	 * Getter for sValue attribute
	 * @param SplObserver $observer
	 * @return void
	 */
	public function getValue()
	{
		return $this->value ;
	}
}

Die Tests

Kommen wir nun zu den Tests.

Den Observer testen

Als erstes testen wir den Observer, um fest zu stellen, ob es auch den Wert des Subject zurückgibt. Der positive Nebeneffekt dieses Tests ist, das wir auch zugleich den Setter und Getter des Subjects testen.

<?php
 
// Das PHPUnit Framework laden
require_once 'PHPUnit/Framework.php';
 
// Die Pattern Klasse fuer die Tests laden
require_once dirname(_FILE_) . '/Observer.php';
require_once dirname(_FILE_) . '/Subject.php';
 
/**
 * Testcase for observer
 * 
 * @category   UnitTests
 * @package    Ddown
 * @subpackage DesignPatterns
 * @version    Number one
 */
class ObserverTest extends PHPUnit_Framework_TestCase
{
	/**
	 * Test update method 
	 * @return void
	 * @covers Observer::update
	 * @covers Subject::setValue
	 * @covers Subject::getValue
	 */
	public function testUpdate()
	{
		$subject  = new Subject();
		$observer = new Observer();
 
		$subject->setValue('Observer Pattern');
 
		self::assertEquals($observer->update($subject), $subject->getValue());
	} 
}

Die Klasse Subject testen

Jetzt kommt die Subject-Klasse dran. Als erstes werden wir der Vollständigkeitshalber ebenfalls den Setter und Getter testen.

<?php
 
// Das PHPUnit Framework laden
require_once 'PHPUnit/Framework.php';
 
// Die Pattern Klasse fuer die Tests laden
require_once dirname(_FILE_) . '/Observer.php';
require_once dirname(_FILE_) . '/Subject.php';
 
/**
 * Testcase for observer
 * 
 * @category   UnitTests
 * @package    Ddown
 * @subpackage DesignPatterns
 * @version    Number one
 */
class SubjectTest extends PHPUnit_Framework_TestCase
{
	/**
	 * @var Subject
	 */
	protected $subject = null;
 
	/**
         * setup for each test
         * @return void
	 */
	protected function setUp()
	{
		$this->subject = new Subject();
	}
 
	/**
	 * test setter and getter for value attribute
	 * @return void
	 */
	public function testSetGetValue()
	{
		$value = 'DDown';
		$this->subject->setValue($value);
		self::assertEquals($this->subject->getValue(), $value);
	}
} 

Gut weiter im Text. Jetzt kommen wir zu unserer eigentliche Aufgabe. Um das Pattern zu Testen werde ich hier nicht die Obeserver-Klasse selbst verwenden, sondern ein sogenanntes Mock-Objekt. Der Grund ist einfach, mit den Mock-Objekt kann ich das Verhalten genauer testen, als mit unsere Observer-Klasse. Z.B. kann ich überprüfen wie oft ein Oberser bzw. die notfiy Methode aufgerufen wurde. Diese Tests könnten wir mit unserer jetzigen Observer-Klasse, ohne sie für die Tests auf zu bohren, nicht ausführen.

        /**
	 * Test the observer pattern
	 * @return void
	 */
	public function testNotify()
	{
		// Da wir gewaehrleisten muessen, dass das Mock-Objekt die SplObserver 
		// Schnittstelle implementiert, geben wir diesen als Klassennamen an.
		$observer = $this->getMock('SplObserver', array('update'));
 
		// Nun kommt der interessante Teil. Wir moechten folgendes testen:
		// * Die Methode update wird nur EINMAL aufgerufen
		// * Das uebergeben Argument ist vom TYP SplSubject
		$observer->expects($this->once())
				  ->method('update')
				  ->with($this->isInstanceOf('SplSubject'));
 
		// Na dann mal los.
		$this->subject->attach($observer);
		$this->subject->setValue('notify now!');
	} 

Fertig. Wenn ihr mir oder dem Mock Objekt nicht glaubt, probiert es selbst aus, gibt an das die Methode update nie aufgerufen werden darf.

...
$observer->expects($this->never());
...

Das Resultat ist folgender:

$ phpunit SubjectTest
PHPUnit 3.3.14 by Sebastian Bergmann.

.F

Time: 0 seconds

There was 1 failure:

1) testNotify(SubjectTest)
SplObserver::update(Subject(...)) was not expected to be called.
/usr/share/php/PHPUnit/Framework/MockObject/Mock.php(228) : eval()'d code:28
...unittests/pattern/observer/Subject.php:59
...unittests/pattern/observer/Subject.php:74
...unittests/pattern/observer/SubjectTest.php:67

FAILURES!
Tests: 2, Assertions: 1, Failures: 1.

So jetzt testen wir das abmelden eines Observers. Auch hier verwenden wir ein Mock-Objekt.

       /**
	 * Test the observer pattern
	 * @return void
	 * @covers Subject::detach
	 * @covers Subject::notify
	 */
	public function testDetach()
	{
		// Da wir gewaehrleisten muessen, dass das Mock-Objekt die SplObserver 
		// Schnittstelle implementiert, geben wir diesen als Klassennamen an.
		$observer = $this->getMock('SplObserver', array('update'));
 
		// * Die Methode update darf kein mal aufgerufen werden
		$observer->expects($this->never())
				  ->method('update');
 
		// Na dann mal los.
		$this->subject->attach($observer);
		$this->subject->detach($observer);
		$this->subject->setValue('notify now!');
	} // function

Der letzte Test denn wir noch benötigen, ist das Gewährleisten eines nicht redunanten Observers im Stack.

       /**
	 * Warrants that only one instance of each observer can be attached 
	 * @return void
	 * @covers Subject::attach
	 * @covers Subject::notify
	 */
	public function testNoRedundantObservers()
	{
		// Da wir gewaehrleisten muessen, dass das Mock-Objekt die SplObserver 
		// Schnittstelle implementiert, geben wir diesen als Klassennamen an.
		$oObserver = $this->getMock('SplObserver', array('update'));
 
		// * Die Methode update wird nur EINMAL aufgerufen
		// * Das uebergeben Argument ist vom TYP SplSubject
		$observer->expects($this->once())
				  ->method('update')
				  ->with($this->isInstanceOf('SplSubject'));
 
		// Na dann mal los.
		$this->subject->attach($observer);
		$this->subject->attach($observer);
		$this->subject->setValue('notify now!');
	}

Refactoring

Da wir gerade von Redundanz sprechen. Es ist euch bestimmt aufgefallen, dass wir in den letzten drei Tests fast immer das selbe Mock-Objekt erzeugen. Der eigentlich Unterschied besteht darin wie oft die Methode update aufgerufen werden soll. Und das stinkt und schreit förmlich nach Refactoring. Denn auch Tests wollen lesbar und pflegbar sein.

Ich verwende Methode extrahieren für das Erzeugen eines Mock-Objekts.

       /**
	 * Create a mock object which implements SplObserver interface
	 * @return SplObserver
	 */
	protected function createMockObserver()
	{
		// Da wir gewaehrleisten muessen, dass das Mock-Objekt die SplObserver 
		// Schnittstelle implementiert, geben wir diesen als Klassennamen an.
		return $this->getMock('SplObserver', array('update'));
	}

Und verwende diese Methode nun in alle drei Tests. Teste meine Tests um zu schauen ob alles noch läuft! Jup läuft. Okay weiter.

Ich verwende nochmals Methode extrahieren für die Definition der Methode update. Die neue Methode erwartet zwei Argumente. Das Erste ist das Mock-Objekt und das Zweite gibt an wie oft die Methode update aufgerufen werden soll.

       /**
	 * @param object $mockObserver
	 * @param int $invokedCount
	 * @return void
	 */
	protected function implementExpectationsForMockObserver($mockObserver, $invokedCount)
	{
		$method = $mockObserver->expects($this->exactly($invokedCount))->method('update');
 
		if (0 < $invokedCount)
		{
			$method->with($this->isInstanceOf('SplSubject'));
		}
	}

Nun ersetze ich den Code in den Tests und teste noch mal alles. Funtzt!

Tags für diesen Artikel: , , ,

Filtering & Escaping Cheat Sheet

Geschrieben von Dejan Spasic • Monday, 4. May 2009 • Kategorie: PHPKommentare (0)

Auf der Seite Filtering & Escaping Cheat Sheet findet Ihr einen nützlichen Cheat Sheet über die Filter- und Maskierungstrategien die man in jeden Fall einsetzen sollte.

Tags für diesen Artikel: ,

PHP Bug 48139

Geschrieben von Dejan Spasic • Monday, 4. May 2009 • Kategorie: PHP , QuerbeetKommentare (0)

Der Bugreport 48139. Ich weiss nicht was ihr davon haltet, aber ich habe mich köstlich amüsiert.

Tags für diesen Artikel: ,