Tag: zend framework

The new providers discovery for Zend_Tool 1.10

With the recent release of the 1.10 version of the  Zend Framework, they made a subtle change on how Zend_Tool searches its providers. Before 1.10 the loader (aka the provider discover) was set to be the IncludePathLoader class; what that meens is that if you wrote a new provider, all that you needed to do to have Zend_Tool automatically find it was to edit your PHP’s include_path or add your provider’s directory to the ZEND_TOOL_INCLUDE_PATH_PREPEND environment variable.

Now that is history, because (source):

There were many issues for people when Zend_Tool used a scanning approach to finding providers. This caused many issues on all different platforms. Now we’ve opted to go the specify your providers approach.

(beware: the suggested solution in that post is wrong)

So if you ever write a new tool provider remember that there’s no more “auto discovery” by scanning the path. The loader now is the BasicLoader and you have to explicitely tell Zend_Tool where your providers are and how their classes are named. For this to happen, you can use the zf enable config.provider command or use the zf.ini file.

My solution is:

  • create a zf.ini file for your project. This is slighlty different from what the documentation implies; it considers zf.ini to be an hidden file in your $HOME. But this is only a default you can change via the  ZF_CONFIG_FILE env variable
  • put something like basicloader.classes.0 = “Migrations_MigrationProvider” as the first line (that example is the actual class name of my migration tool provider)
  • assure that your class can be loaded, setting ZEND_TOOL_INCLUDE_PATH_PREPEND accordingly

On the same topic you can also read this issue.

As a bonus track, below is a little bash script I use to run my migration tool.

#!/bin/bash

APPPATH=$(readlink -f ..)/app

export ZF_CONFIG_FILE=${APPPATH}/../zf.ini

if [ ! -f ${ZF_CONFIG_FILE} ]; then
  echo "Non trovo zf.ini"
  echo "Forse non stai eseguendo questo programma dalla directory DB?"
  exit -1
fi

if [[ "${ZF_BIN_DIR}" == "" ]]; then
  ZF_BIN_DIR=$(readlink -f ../vendor/Zend)/../../bin
  ZF_BIN_DIR=$(readlink -f ${ZF_BIN_DIR})
fi

if [ ! -f ${ZF_BIN_DIR}/zf.sh ]; then
  echo "Non trovo zf.sh in " ${ZF_BIN_DIR}
  echo "Forse non stai eseguendo questo programma dalla directory DB?"
  exit -1
fi

MIGCLASSDIR=$(readlink -f ../vendor/Renomo/library)

if [ ! -d ${MIGCLASSDIR} ]; then
  echo "Impossibile trovare la directory della classe Migration"
  exit -1
fi

MIGDIR=$(readlink -f migrations)

if [ ! -d ${MIGDIR} ]; then
  echo "Impossibile trovare la directory delle migration"
  exit -1
fi

export ZEND_TOOL_INCLUDE_PATH_PREPEND=${MIGCLASSDIR}

${ZF_BIN_DIR}/zf.sh run migration ${1}

Come fare i test sui controller con Zend Framework

Lo ammetto, pietà di me: non sono un grandissimo esperto di test. Non ne ho mai fatti troppi e probabilmente neppure troppo belli.

Nel mio ultimo progetto, iniziato da poco, ho rimesso mano alla questione e unilateralmente deciso di applicarmi meglio a farli funzionare. Il progetto è scritto in PHP ed usa Zend Framework (da qui in avanti solo ZF). Premetto che quello che segue è probabilmente inutile a chi non ha già una certa esperienza con quel framework.

ZF è sempre stato testabile predilegendo tra i vari framework PHPUnit. Questa scelta si è spinta recentemente fino ad offrire un componente (Zend_Test) che estende le classi base dei test di PHPUnit in modo da esporre ai test dei controller tutti gli oggetti di alto livello necessari per lavorare in modo estremamente pratico. Questi oggetti sono la $request e la $response, ma anche una pletora di nuovi assert() tra cui alcuni fantastici che usano le estensioni XPath e DOM per verificare il contenuto di una pagina durante un test utilizzando appunto XPath o selettori CSS.

Quando scriveremo un test per un controller di una nostra applicazione, faremo dunque derivare il nostro test dalla classe Zend_Test_PHPUnit_ControllerTestCase.

Quelli che seguono sono consigli derivati dalla mia recente esperienza e non vogliono assolutamente essere una trattazione esaustiva sui test dei quali, come innanzi confessavo, non sono un grande esperto. Ma chi non è un grande esperto di test fa fatica a trovare documentazione di base sui test con Zend Framework.. per cui eccomi qua :)

Prima di tutto risolviamo il problema più grave: PHPUnit va installato (non viene distribuito insieme a ZF).

Si può scegliere di farlo in tre modi:

  1. via PEAR
  2. installare il pacchetto della propria distribuzione GNU/Linux
  3. andarsi a prendere direttamente il pacchetto di sorgenti dal sito.

Su Ubuntu 9.04, la mia attuale distribuzione, l’unica possibilità è la 3. Infatti la versione del pacchetto che arriva con la distribuzione è troppo vecchia e Zend_Test fa uso di feature piuttosto recenti (non ci puoi fare niente: ti beccherai subito un errore riguardante il metodo sconosciuto incrementAssertionCounter(). Show stopper.). Anche la 1 non mi è parsa percorribile a causa del mio “PEAR” troppo vecchio per la versione di PhpUnit (non ho indagato moltissimo, a dire il vero).

Ho preso dunque il tgz dal sito di PhpUnit, l’ho scompattato e ho messo tutta la sua directory PHPUnit nella stessa directory dove risiede lo ZF (directory ./library, per la cronaca). In questo modo le classi di PHPUnit sono già visibili senza che sia necessario toccare gli include path, grazie agli autoloader di ZF stesso.

Ho creato una directory “tests” allo stesso livello della directory “application” della mia applicazione, dunque fuori dalla DocumentRoot.

All’interno di questa directory ho messo, per ora: un file AllTests.php che serve come entry point per eseguire tutti i test e un file TestConfiguration.php che in pratica sostituisce le operazioni da fare nell’index.php del sito.

Ecco per esempio come potrebbe essere un primo AllTests.php:

<?php
require 'TestConfiguration.php';

$suite = new PHPUnit_Framework_TestSuite();
$suite->setName('MyApp');
$suite->addTestSuite('controllers_IndexControllerTest');
$suite->addTestSuite('controllers_UsersControllerTest');

PHPUnit_TextUI_TestRunner::run($suite, array());

Ed ecco invece parte del mio TestConfiguration.php

<?php
TestConfiguration::setUp();  

class TestConfiguration
{
  static function setUp()
  {
    define('APPLICATION_ENVIRONMENT', 'test');
    define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application/'));

    set_include_path(implode(PATH_SEPARATOR, array(
      realpath(APPLICATION_PATH . '/../library'),
      realpath(APPLICATION_PATH . '/library'),
      realpath(APPLICATION_PATH . '/models'),
      realpath(APPLICATION_PATH . '/modules/default/forms'),
      get_include_path(),
    )));

    require_once 'Zend/Loader/Autoloader.php';
    $loader = Zend_Loader_Autoloader::getInstance();
    $loader->setFallbackAutoloader(true);
  }

  static function setUpDatabase()
  {
    // TODO
  }
}  

Nella directory tests/controllers metterò i test dei controller. Per esempio, un inizio di un test per una home page potrebbe essere:

class controllers_IndexControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
{

  public function testHomePageIsASuccessfulRequest()
  {
    $this->dispatch('/');

    // Tests there are no exceptions on the home page
    $this->assertFalse($this->response->isException());

    $this->assertController('session');
    $this->assertAction('new');

    $this->assertQueryCount('#LoginForm', 1);
  }

}

A questo punto non resta che eseguire i test. Da linea comando sarà sufficiente lanciare un php AllTests.php direttamente dalla directory tests.

Un altro punto di attenzione è dovuto al fatto che PHPUnit inizia molto presto a  scrivere in console e questo potrebbe dare fastidio a particolari operazioni di ZF. Nella fattispecie mi sto riferendo al malefico errore “Headers already sent”.

Quello che succedeva a me era che in una preDispatch() di un action plugin, a fronte di una particolare situazione, effettuavo un header(“Location …”). Questa operazione non andava a buon fine proprio per il motivo di cui sopra (e tutti i test fallivano miseramente); per cui, se la vostra applicazione ha un redirect HTTP in un plugin prima dell’effettivo dispatch, potreste incorrere in un errore del genere. La soluzione (almeno per me) è stata quella di non fare la redirect ma di modificare il controller e la action della corrente request. Stesso risultato, ed anche pienamente testabile.


I contenuti di questo sito sono distribuiti con una licenza Creative Commons 2.5 eccetto dove diversamente specificato.

Tema WordPress Punto5 sviluppato da Claudio Cicali; icone del set famfamfam silk e komodomedia.

© 2005-2010
Claudio Cicali