Andreas Löer

Softwareentwicklung

Erweitertes Testen des Restcontrollers

without comments

Für effektiveres Testen der JSON-Antwort eines Controllers entwickle ich eine Ableitung der Klasse Zend_Test_PHPUnit_ControllerTestCase. In dieser Klasse sollen die Funktionalitäten zum Zugriff auf in die JSON-Daten, ähnlich der DomQuery-Assertmethoden.
In einem ersten Schritt existiert eine Methode die prüft, ob die Daten auch im JSON-Format vorliegen. Wenn der Controller noch Fehlermeldungen an die Daten anhängt, wird durch die Dekodierung eine Exception geworfen. Diese wird hier in eine PHPUnit_Framework_ExpectationFailedException umgewandelt.

class PMLib_Test_PHPUnit_ControllerTestCase extends Zend_Test_PHPUnit_ControllerTestCase
{

    public function assertResponseIsJson ( $message = '' ) {
        $this->_incrementAssertionCount();
        $bodyData = $this->getResponse()->getBody();
        try {
            $decode = Zend_Json::decode($bodyData);
        }
        catch (Zend_Json_Exception $exception ) {
            $message .= "\nFailed asserting JSON format";
            throw new PHPUnit_Framework_ExpectationFailedException( $message );
        }
    }
}

Written by Andreas

Mai 15th, 2012 at 3:58 pm

Posted in Zend-Framework

Zend: Testen des Restcontrollers

without comments

Das automatische Testen des Restcontrollers funktioniert im Prizip genauso wie das Testen der anderen Controller. Allerdings sind hier einige Details zu beachten:

  • Der angegebene Controller muss darf nicht den Namen des Controllers enthalten (der ist normalerweise großgeschrieben), sondern muss genauso wie der Controlleranteil des Links geschrieben werden.
  • Der ID-Parameter muss an die URL angehängt werden, da der Link sonst nicht genauso aussieht wie einer, der von einem Restclient geliefert wird.
  • Die Methode muss explizit gesetzt werden.

 

Beispiel für den Action delete Link /rest/1

    public function testDeleteAction()
    {
        $params = array('action' => 'index', 'controller' => 'rest', 'module' => 'default');
        $urlParams = $this->urlizeOptions($params);
        $url = $this->url($urlParams);
        $url .= '/1';
        $this->getRequest()
                ->setMethod('delete')
                ->setHeader('X_REQUESTED_WITH','XMLHttpRequest');

        $this->dispatch($url);

        // assertions
        $this->assertModule($urlParams['module']);
        $this->assertController($urlParams['controller']);
        $this->assertAction('delete');

        $this->assertResponseCode('204');
        $this->assertContentType('application/json');
    }

 

 

Written by Andreas

Mai 10th, 2012 at 2:35 pm

Posted in Zend-Framework

Tagged with , ,

ControllerTestCase – Liste mit Assert-Funktionen

without comments

Hier ist eine Liste mit den Assert-Funktionen, die im Controllertest des Zend-Frameworks (Zend_Test_PHPUnit_ControllerTestCase) implementiert sind. Diese Klasse ist eine Ableitung vom bekannten PHPUnit_Framework_TestCase.

Assert-Funktionen zum Testen des DOM aus der Response

public function assertQuery($path, $message = '')
public function assertNotQuery($path, $message = '')
public function assertQueryContentContains($path, $match, $message = '')
public function assertNotQueryContentContains($path, $match, $message = '')
public function assertQueryContentRegex($path, $pattern, $message = '')
public function assertNotQueryContentRegex($path, $pattern, $message = '')
public function assertQueryCount($path, $count, $message = '')
public function assertNotQueryCount($path, $count, $message = '')
public function assertQueryCountMin($path, $count, $message = '')
public function assertQueryCountMax($path, $count, $message = '')
public function assertXpath($path, $message = '')
public function assertNotXpath($path, $message = '')
public function assertXpathContentContains($path, $match, $message = '')
public function assertNotXpathContentContains($path, $match, $message = '')
public function assertXpathContentRegex($path, $pattern, $message = '')
public function assertNotXpathContentRegex($path, $pattern, $message = '')
public function assertXpathCount($path, $count, $message = '')
public function assertNotXpathCount($path, $count, $message = '')
public function assertXpathCountMin($path, $count, $message = '')
public function assertXpathCountMax($path, $count, $message = '')

Assert-Funktionen zum Testen des Headers aus der Response

public function assertRedirect($message = '')
public function assertNotRedirect($message = '')
public function assertRedirectTo($url, $message = '')
public function assertNotRedirectTo($url, $message = '')
public function assertRedirectRegex($pattern, $message = '')
public function assertNotRedirectRegex($pattern, $message = '')
public function assertResponseCode($code, $message = '')
public function assertNotResponseCode($code, $message = '')
public function assertHeader($header, $message = '')
public function assertNotHeader($header, $message = '')
public function assertHeaderContains($header, $match, $message = '')
public function assertNotHeaderContains($header, $match, $message = '')
public function assertHeaderRegex($header, $pattern, $message = '')
public function assertNotHeaderRegex($header, $pattern, $message = '')

Assert-Funktinonen zum Testen des Ablaufs.

Hier kann getestet werden, ob dir Response von dem erwarteten Modzl, Controller, Action erzeugt wurde.

public function assertModule($module, $message = '')
public function assertNotModule($module, $message = '')
public function assertController($controller, $message = '')
public function assertNotController($controller, $message = '')
public function assertAction($action, $message = '')
public function assertNotAction($action, $message = '')
public function assertRoute($route, $message = '')
public function assertNotRoute($route, $message = '')

 

Written by Andreas

Mai 9th, 2012 at 8:43 am

Posted in Zend-Framework

Tagged with ,

Extjs – Form sendet keinen X_REQUESTED_WITH

without comments

Wird in Extjs eine Form verwendet, so wird beim Speichern kein X_REQUESTED_WITH-Header gesendet. Grund ist laut der Dokumentation in den Quellen eine generierte versteckte Form, die per Submit die Daten absendet. Diese Form wird nach dem Empfang der Antwort wieder entfernt.

Zu beachten ist ausserdem , das wenn eine JSON-Antwort gesendet wird muss der Content-Type der Antwort “text/html” sein. Diese Anwort wird vom Browser in das Document eingetragen und erst dann vom Extjs in ein XMLHttpRequest-Objekt kopiert.

Written by Andreas

Mai 8th, 2012 at 12:34 pm

Posted in Extjs

Zend – Restcontroller

without comments

Das Zendframework stellt neben dem normalen Controllern auch einen Controller für eine REST-Schnittstelle zur Verfügung. Dieser ist eine Ableitung des normalen Actioncontrollers und stellt eine Methodendefinition für die HTTP-Request-Methoden Get, Put, Post und Delete bereit. Diese sind als abstrakte Methoden definiert und müssen im Controller abgeleitet werden.

Der Restcontroller wird in der einfachsten Variante einfach in der Bootstrap initalisiert. Um automatische Tests verwenden zu können müssen die Defaultroutes noch am Ende hinzugefügt werden.

    public function _initRest () {
        $front     = Zend_Controller_Front::getInstance();
        $restRoute = new Zend_Rest_Route($front, array(), array('default' => array('rest')));
        $front->getRouter()->addRoute('rest', $restRoute);
        $front->getRouter()->addDefaultRoutes();
    }

 

Das Gerüst für eine Rest-Controller sieht so aus:

class RestController extends Zend_Rest_Controller
{

  public function init()
  {
  }

  public function indexAction()
  {
  }

  public function getAction()
  {
  }

  public function postAction()
  {
  }

  public function putAction()
  {
  }

  public function deleteAction()
  {
  }

}

Nach der Bearbeitung von REST- Funktionalitäten sollten am Ende die richtigen Statuscodes zurückgeliefert werden.

200 OK: Diese Antwort sollte auf ein GET senden, wenn die Datenausgeliefert werden. Wenn nichts gefunden wurde, liefert es ein 404.

201 Created: Diese Antwort sollte auf ein POST senden, wenn die Resource angelegt worden ist. Es muss eine Antwort gesendet werden, die den gesamten Datensatz enthält.

204 No Content: Diese Antwort sendet ein DELETE, wenn die Resource gelöscht worden ist.

Written by Andreas

Mai 7th, 2012 at 8:50 am

Posted in Zend-Framework

Tagged with ,

Zend Test mit frischer Datenbank

without comments

Füe einen Test ist das wichtig das die Datenbank in einen definierten Zustand vorliegt, damit die Testergebnisse immer die gleichen sind.
Im Zend-Framework werden die automatischen Tests unter dem Verzeichnis “tests” zu finden. Hier gibt es die Datei phpunit.xml die die Tests definiert.
Bevor die Tests ausgeführt werden, wird die Datei Bootstrap.php ausgeführt. Dies ist der ideale Ort um eine frische Datenbank zu erstellen.
Diese Datenbank sollte eine Instanz sein, die in der Application.ini defniert wird.

[testing : production]
doctrine.conn.dbname = 'datenbank_test'

In der Bootstrap.php können nun die entsprechenden Routinen angehängt werden:

//// Do the setup of the Database
$application = new Zend_Application(
'testing',
APPLICATION_PATH . '/configs/application.ini'
);

// Bootstrap the application
$bootstrap = $application->bootstrap()->getBootstrap();

// Retrieve the Doctrine 2 entity manager
$registry = Zend_Registry::getInstance();
$em = $registry->entitymanager;

// Instantiate the schema tool
$tool = new Doctrine\ORM\Tools\SchemaTool($em);

// Retrieve all of the mapping metadata
$classes = $em->getMetadataFactory()->getAllMetadata();

// Delete the existing test database schema
$tool->dropSchema($classes);

// Create the test database schema
$tool->createSchema($classes);

Initialdaten aus Yaml-Datei einlesen.
In der 1.3 von Doctrine gab es die Funktion loadData()die in der 2.0 entfallen ist. Diese Funktionalität lasst sich aber leicht nachbilden:

// Setup the fixture data
require_once('Symfony\yaml\sfYaml.php');
$data = sfYaml::load(dirname(__FILE__)."/data/fixtures.sql");
foreach ($data as $tableName => $rows) {
if (!isset($rows)) {
$rows = array();
}

if (!is_array($rows)) {
continue;
}

foreach ( $rows as $row ) {
$table = new $tableName();
foreach( $row as $columnName => $value ) {
$setter = "set" . ucfirst($columnName);
$table->$setter($value);
}
$em->persist($table);
unset($table);
}
}
$em->flush();

Die Datenbankverbindung muss nun noch geschlossen werden, da es sonst zu Schwierigkeiten mit den Tests kommt:

// Close the connection
$em->getConnection()->close();

Written by Andreas

Mai 4th, 2012 at 1:42 pm

Zend mit Doctrine

without comments

Die Anleitung wie Doctrine in das Zendframework eingebunden werden kann gibt es im Netz in Mengen. Hier nur nochmal der Schnellüberblick.
Das Model wird über Annotations definiert. Für die Zwischenspeicherung für Doctrine-Dateien wird unter dem Hauptverzeichnis der Ordner “Proxies” angelegt.

Application.ini

; Datenbankverbindung für den OR-Mapper
doctrine.conn.host = 'localhost'
doctrine.conn.user = 'user'
doctrine.conn.pass = 'password'
doctrine.conn.driv = 'pdo_mysql'
doctrine.conn.dbname = 'database'
doctrine.path.models = APPLICATION_PATH "/models"

bootstrap.php

/**
* Initialize auto loader of Doctrine
*
* @return Doctrine_Manager
*/
public function _initDoctrine() {
require_once 'Doctrine/Common/ClassLoader.php';

$doctrineConfig = $this->getOption('doctrine');

$classLoader = new \Doctrine\Common\ClassLoader('Doctrine');
$classLoader->register();

$config = new \Doctrine\ORM\Configuration();

$cache = new \Doctrine\Common\Cache\ArrayCache;
$config->setMetadataCacheImpl($cache);
$config->setQueryCacheImpl($cache);

$driverImpl = $config->newDefaultAnnotationDriver(APPLICATION_PATH.'/models');
$config->setMetadataDriverImpl($driverImpl);

$config->setProxyDir(APPLICATION_PATH . '/../Proxies');
$config->setProxyNamespace('App\Proxies');

$config->setAutoGenerateProxyClasses(true);

$connectionOptions = array(
'driver' => $doctrineConfig['conn']['driv'],
'user' => $doctrineConfig['conn']['user'],
'password' => $doctrineConfig['conn']['pass'],
'dbname' => $doctrineConfig['conn']['dbname'],
'host' => $doctrineConfig['conn']['host']
);

$em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config);

$registry = Zend_Registry::getInstance();
$registry->entitymanager = $em;

return $em;
}

Der Enitymanager ist nun in der Registry eingetragen und kann in der Anwendlung verwendet werden.

Written by Andreas

Mai 4th, 2012 at 1:11 pm

Posted in Allgemein

Installation Zend/Doctrine/PHPUnit mit Pear

without comments

Für die Installation der Komponeten Zend-Framework, Doctrine und PHPUnit sind einige Schritte notwendig:


pear channel-discover pear.zfcampus.org
pear install zfcampus/zf

pear channel-discover pear.symfony.com

pear channel-discover pear.doctrine-project.org
pear install doctrine/DoctrineCommon
pear install doctrine/DoctrineORM

pear channel-discover pear.phpunit.de
pear install phpunit/PHPUnit
pear install phpunit/dbUnit

Written by Andreas

Mai 4th, 2012 at 9:56 am

SVN-Serverdienst unter Win7

without comments

Ein SVN-Server wird immer wieder benötigt. Die Einrichtung unter Windows ist 2 Schritte aufwändiger als unter Debian.

Erstmal ein passendes Installationspaket finden und ausführen. Ich habe ein Paket bei Sourceforge gefunden:

http://sourceforge.net/projects/win32svn/

Nach der Installation kann svnserv dann als Dienst installiert und gestartet werden:

sc create svnserve binpath= "\"c:\Program Files\Subversion\bin\svnserve.exe\" --service --root c:\var\svn" displayname= "Subversion" depend= tcpip start= auto
sc start svnserve

Hierzu muss die Eingabeaufforderung als Administrator ausgeführt werden.

 

Written by Andreas

Februar 22nd, 2012 at 11:06 am

Pear: possible symlink attack

without comments

Bei der Installation von Pear-Paketen unter Win7 kann folgende Fehlermeldung auftreten:


SECURITY ERROR: Will not write to C:\Users\andreas\AppData\Local\Temp\pear\cache\27ea434482206c056c92f274832da110rest.cacheid as it is symlinked to C:\Users\andreas\AppData\Local\Temp\pear\cache\27ea434482206c056c92f274832da110rest.cacheid - Possible symlink attack

Hier scheint das Pear mit seinen Cachedateien durcheinander zukommen. In meine Fall konnte ich das Problem damit lösen, die Dateinen, die sich in dem Cacheverzeichnis (C:\Users\andreas\AppData\Local\Temp\pear\cache)  befanden zu löschen.

Written by Andreas

Februar 7th, 2012 at 9:30 am

Posted in Allgemein