Testing

A partir de CakePHP 1.2 disponemos de soporte para un completo entorno de testing incorporado en CakePHP. Este entorno es una extensión del entorno SimpleTest para PHP. En esta sección discutiremos cómo preparar la aplicación para testing y cómo construir y ejecutar tus tests.

Preparándose para el testing

¿Preparado/a para empezar a hacer test? ¡Bien! ¡Vamos allá entonces!

Installing SimpleTest

Instalación de SimpleTest

El entorno de testing provisto con CakePHP 1.2 está construido sobre el entorno de testing SimpleTest. SimpleTest no se distribuye con la instalación por defecto de CakePHP por lo que debemos descargarlo primero. Lo puedes encontrar aquí: http://simpletest.sourceforge.net/

Consigue la última versión y descomprime el código en tu carpeta cake/vendors, o en tu carpeta app/vendors, según tus preferencias. Ahora deberías tener un directorio vendors/simpletest con todos los archivos y carpetas de SimpleTest dentro. ¡Recuerda tener el nivel de DEBUG al menos a 1 en tu archivo app/config/core.php antes de ejecutar cualquier test!

Si no tienes una conexión de base de datos para test definida en app/config/database.php, las tablas de test se crearán con un prefijo test_suite_. Puedes crear una conexión de base de adtos $test para que contenga sólo las tablas de test como la que te mostramos debajo:

var $test = array(
    'driver' => 'mysql',
    'persistent' => false,
    'host' => 'dbhost',
    'login' => 'dblogin',
    'password' => 'dbpassword',
    'database' => 'databaseName'
);

Si la base de datosd e test está disponible y CakePHP puede conectarse a ella, todas las tablas serán creadas en esta base de datos.

Ejecutando los test-cases incorporados

CakePHP 1.2 se distribuye con un gran paquete de test-cases sobre la funcionalidad del núcleo de CakePHP

Puedes acceder a estos test navegando a http://your.cake.domain/cake_folder/test.php - dependiendo de como sea la disposición específica de tu aplicación. Intenta ejecutar alguno de los grupos de test del núcleo haciendo click en el enlace correspondiente. Ejecutar un test puede llevar un rato, pero deberías ver algo parecido a “2/2 test casese complete: 49 passes, 0 fails and 0 exceptions.”.

¡Felicidades, ya estás listo/a para empezar a escribir tests!

Introducción a los test - Unit testing vs. Web testing

El entorno de test de CakePHP soporta dos tipos de testing. Uno es el Unit Testing, en el cual tú pruebas pequeñas partes de tu código, como pueden ser un método en un componente o una acción en un controlador. El otro tipo de testing soportado es el Web Testing, en el cual automatizas el trabajo de evaluar tu aplicación mediante la navegación por las páginas, relleno de formularios, hacer clic en enlaces y demás.

Preparando datos de prueba

Acerca de las fixtures

Cuando pruebes código que dependa de modelos y datos, puedes usar fixtures como una forma de generar tablas temporales de datos cargados con datos de ejemplo que pueden ser utilizados por el test. El beneficio de usar fixtures es que tus test no pueden de ningún modo alterar los datos de la aplicación en marcha. Además, así puedes empezar a probar tu código antes de desarrollar contenido en vivo para tu aplicación.

CakePHP intenta utilizar la conexión denominada $test en tu archivo app/config/database.php. Si esta conexión no es utilizable, usará la configuración de base de datos $default y creará las tablas de test en la base de datos definida en esa configuración. En cualquier caso, añadirá el prefijo “test_suite_” a tu propio prefijo para las tablas (si es que hay alguno) para evitar colisiones con las tablas existentes.

CakePHP realiza los siguientes pasos durante el curso de un test case basado en fixture:

  1. Crea tablas para cada una de las fixtures necesarias
  2. Rellena las tablas con datos, si es que se han proporcionado éstos en la fixture
  3. Ejecuta los métodos de los test
  4. Vacía las tablas fixture
  5. Elimina las tablas fixture de la base de datos

Creando fixtures

Cuando se crea un fixture se deben definir 2 cosas:

  1. cómo se crea la tabla (que campos serán parte de la tabla)
  2. cómo se guardarán los registros en la tabla de prueba. Luego

podremos crear nuestro primer fixture, que utilizaremos para testear nuestro modelo Article. Creamos un archivo llamado article\_fixture.php en la carpeta app/tests/fixtures, con el siguiente código:

<?php
 class ArticleFixture extends CakeTestFixture {
      var $name = 'Article';

      var $fields = array(
          'id' => array('type' => 'integer', 'key' => 'primary'),
          'title' => array('type' => 'string', 'length' => 255, 'null' => false),
          'body' => 'text',
          'published' => array('type' => 'integer', 'default' => '0', 'null' => false),
          'created' => 'datetime',
          'updated' => 'datetime'
      );
      var $records = array(
          array ('id' => 1, 'title' => 'First Article', 'body' => 'First Article Body', 'published' => '1', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'),
          array ('id' => 2, 'title' => 'Second Article', 'body' => 'Second Article Body', 'published' => '1', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'),
          array ('id' => 3, 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => '1', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31')
      );
 }
 ?>

Usamos $fields para indicar los campos que serán parte de la tabla, y cómo serán definidos. El formato que se usa para definir los campos es el mismo que usamos en la funcion generateColumnSchema() definida en el motor de base de datos de Cake (por ejemplo en dbo_mysql.php.) Los atributos que un campo puede tenes son los siguientes:

type
es el tipo de dato de CakePHP. Actualmente los soportados son: string (mapea como VARCHAR), text (mapea como TEXT), integer (mapea como INT), float (mapea como FLOAT), datetime (mapea como DATETIME), timestamp (mapea como TIMESTAMP), time (mapea como TIME), date (mapea como DATE), y binary (mapea como BLOB)
key
setea el campo como primary para hacerlo auto-incrementable (AUTO_INCREMENT), y clave primaria (PRIMARY KEY) de la tabla.
length
setea el tamaño del campo.
null
setea true o false. Si puede ser nulo indicamos true, si no se permiten nulos va false
default
el valor por defecto del campo.

Finalmente podemos setear un conjunto de registros que seran cargados luego de que la tabla de testeo se crea. El formato es bastante simple, sin embargo necesita un poco más de expilcación. Solo ten en cuenta que cada registro del array $records debe tener una key para cada campo del array $fields. Si un campo para un registro en particular necesita tener el valor nulo, solo especifica el valor de ese campo como nulo (NULL true).

Importar información de tabla y registros

Tu aplicación puede tener ya modelos funcionando con datos reales asociados, y puedes decidir probar tu modelo con esos datos. Sería entonces un esfuerzo doble tener que definir la tabla y/o los registros en tus fixtures. Por suerte, hay una forma de hacer que la definición de la tabla y/o los registros para una fixture en particular vengan de un modelo o una tabla ya existentes.

Comencemos con un ejemplo. Asumiento que tienes un modelo llamado Article disponible en tu aplicación (que se corresponde con una tabla llamada articles), cambiamos la fixture de ejemplo que dimos en la sección anterior (app/tests/fixtures/article_fixture.php) a:

<?php
  class ArticleFixture extends CakeTestFixture {
         var $name = 'Article';
         var $import = 'Article';
  }
  ?>

Esta sentencia le dice a la test suite que importe tu definición de tabla de la tabla asociada al modelo llamado Article. Puedes usar cualquier modelo disponible en tu aplicación. La expresión anterior no importa registros, pero puedes hacerlo cambiandola para que sea:

<?php
class ArticleFixture extends CakeTestFixture {
    var $name = 'Article';
    var $import = array('model' => 'Article', 'records' => true);
}
?>

Si, por otro lado, tienes una tabla creada pero no un modelo disponible para ella, puedes especificar que tu importación consistirá en leer la información de la tabla. Por ejemplo:

<?php
  class ArticleFixture extends CakeTestFixture {
         var $name = 'Article';
         var $import = array('table' => 'articles');
  }
?>

Esto importará la definición de una tabla llamada ‘articles’ usando tu conexión de base de datos denominada ‘default’. Si quieres cambiar la conexión sólo tienes que hacer:

<?php
  class ArticleFixture extends CakeTestFixture {
  var $name = 'Article';
  var $import = array('table' => 'articles', 'connection' => 'other');
  }
  ?>

Ya que se usa tu conexión a la base de datos, si hay algún prefijo de tabla declarado, este será usado automáticamente al recabar la información de tabla. Los dos fragmentos anteriores no importan registros de la tabla. Para forzar a la fixture a importar también los registros, cambialo a:

<?php
  class ArticleFixture extends CakeTestFixture {
         var $name = 'Article';
         var $import = array('table' => 'articles', 'records' => true);
  }
?>

Naturalmente puedes importar tus definiciones de tabla de un modelo o tabla existente, pero tener tus registros definidos directamente en la fixture, como se mostraba en la sección anterior. Por ejemplo:

<?php
  class ArticleFixture extends CakeTestFixture {
         var $name = 'Article';
         var $import = 'Article';

         var $records = array(
             array ('id' => 1, 'title' => 'First Article', 'body' => 'First Article Body', 'published' => '1', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'),
             array ('id' => 2, 'title' => 'Second Article', 'body' => 'Second Article Body', 'published' => '1', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'),
             array ('id' => 3, 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => '1', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31')
         );
  }
?>

Creando los tests

En primer lugar, revisar una serie de normas y directrices para los tests:

  1. Los archivos de PHP que contiene los tests deben estar en : app/tests/cases/[algun_ archivo].
  2. Los nombres de estos archivos deben terminar con un .test.php en lugar de sólo .php.
  3. Las clases que contienen los tests debe extender o heredar de CakeTestCase o CakeWebTestCase.
  4. El nombre de cualquier método que contenga un test (por ejemplo, que contiene una afirmación) debería comenzar con test, como en testPublished().

Cuando se crea un caso test, puede ejecutarce por medio del navegador en la siguiente dirección http://tu.dominio.cake/carpeta_cake/test.php (dependiendo de cómo se ve específicamente tu configuración) y haciendo clic en App casos de test, y a continuación, haga clic en el enlace a su archivo.

CakeTestCase Callback Methods

If you want to sneak in some logic just before or after an individual CakeTestCase method, and/or before or after your entire CakeTestCase, the following callbacks are available:

start()

end()

startCase()

endCase()

before($method)

after($method)

startTest($method)

endTest($method)

Testing models

Creating a test case

Let’s say we already have our Article model defined on app/models/article.php, which looks like this:

<?php
  class Article extends AppModel {
         var $name = 'Article';

         function published($fields = null) {
             $params = array(
                   'conditions' => array(
                         $this->name . '.published' => 1
                   ),
                   'fields' => $fields
             );

             return $this->find('all',$params);
         }

  }
?>

We now want to set up a test that will use this model definition, but through fixtures, to test some functionality in the model. CakePHP test suite loads a very minimum set of files (to keep tests isolated), so we have to start by loading our parent model (in this case the Article model which we already defined), and then inform the test suite that we want to test this model by specifying which DB configuration it should use. CakePHP test suite enables a DB configuration named test_suite that is used for all models that rely on fixtures. Setting $useDbConfig to this configuration will let CakePHP know that this model uses the test suite database connection.

CakePHP Models will only use the test_suite DB config if they rely on fixtures in your testcase!

a test model that will extend from Article, set $useDbConfig and $name appropiately. Let’s now create a file named article.test.php in your app/tests/cases/models directory, with the following contents:

<?php
  App::import('Model','Article');


  class ArticleTestCase extends CakeTestCase {
         var $fixtures = array( 'app.article' );
  }
?>

We have created the ArticleTestCase. In variable $fixtures we define the set of fixtures that we’ll use.

If your model is associated with other models, you will need to include ALL the fixtures for each associated model even if you don’t use them. For example: A hasMany B hasMany C hasMany D. In ATestCase you will have to include fixtures for a, b, c and d.

Creating a test method

Let’s now add a method to test the function published() in the Article model. Edit the file app/tests/cases/models/article.test.php so it now looks like this:

<?php
  App::import('Model', 'Article');

  class ArticleTestCase extends CakeTestCase {
      var $fixtures = array( 'app.article' );

      function testPublished() {
          $this->Article =& ClassRegistry::init('Article');

          $result = $this->Article->published(array('id', 'title'));
          $expected = array(
              array('Article' => array( 'id' => 1, 'title' => 'First Article' )),
              array('Article' => array( 'id' => 2, 'title' => 'Second Article' )),
              array('Article' => array( 'id' => 3, 'title' => 'Third Article' ))
          );

          $this->assertEqual($result, $expected);
      }
  }
  ?>

by creating an instance of our fixture based Article model, and then run our published() method. In $expected we set what we expect should be the proper result (that we know since we have defined which records are initally populated to the article table.) We test that the result equals our expectation by using the assertEqual method. See the section Creating Tests for information on how to run the test.

Testing controllers

Creando un test case

Digamos que tienes un típico controlador de artículos, con su correspondiente modelo, y que se parece a éste:

<?php
class ArticlesController extends AppController {
   var $name = 'Articles';
   var $helpers = array('Ajax', 'Form', 'Html');

   function index($short = null) {
     if (!empty($this->data)) {
       $this->Article->save($this->data);
     }
     if (!empty($short)) {
       $result = $this->Article->findAll(null, array('id',
          'title'));
     } else {
       $result = $this->Article->findAll();
     }

     if (isset($this->params['requested'])) {
       return $result;
     }

     $this->set('title', 'Articles');
     $this->set('articles', $result);
   }
}
?>

Crea un archivo llamado articles_controller.test.php y pon lo siguiente dentro:

<?php
class ArticlesControllerTest extends CakeTestCase {
   function startCase() {
     echo '<h1>Comenzando Test Case</h1>';
   }
   function endCase() {
     echo '<h1>Terminado Test Case</h1>';
   }
   function startTest($method) {
     echo '<h3>Comenzando método ' . $method . '</h3>';
   }
   function endTest($method) {
     echo '<hr />';
   }
   function testIndex() {
     $result = $this->testAction('/articles/index');
     debug($result);
   }
   function testIndexShort() {
     $result = $this->testAction('/articles/index/short');
     debug($result);
   }
   function testIndexShortGetRenderedHtml() {
     $result = $this->testAction('/articles/index/short',
     array('return' => 'render'));
     debug(htmlentities($result));
   }
   function testIndexShortGetViewVars() {
     $result = $this->testAction('/articles/index/short',
     array('return' => 'vars'));
     debug($result);
   }
   function testIndexFixturized() {
     $result = $this->testAction('/articles/index/short',
     array('fixturize' => true));
     debug($result);
   }
   function testIndexPostFixturized() {
     $data = array('Article' => array('user_id' => 1, 'published'
          => 1, 'slug'=>'new-article', 'title' => 'New Article', 'body' => 'New Body'));
     $result = $this->testAction('/articles/index',
     array('fixturize' => true, 'data' => $data, 'method' => 'post'));
     debug($result);
   }
}
?>

El método testAction

La novedad aquí es el método testAction. El primer argumento de este método es la URL “en formato Cake” de la acción del controlador que se quiere probar, como en ‘/articles/index/short’.

El segundo argumento es un array de parámetros, consistente en:

return
Indica lo que se va a devolver.
Los valores válidos son:
  • ‘vars’ - Obtienes las variables de la vista disponibles tras ejecutar la acción
  • ‘view’ - Obtienes la vista generada, sin layout
  • ‘contents’ - Obtienes todo el html de la vista, incluyendo layout
  • ‘result’ - Obtienes el valor de retorno de la acción como cuando se usa $this->params[‘requested’].

El valor por defecto es ‘result’.

fixturize
Ponlo a true si quieres que tus modelos se “auto-simulen” (de modo que las tablas de la aplicación se copian, junto con los registros, para que al probar las tablas si cambias datos no afecten a tu aplicación real.) Si en ‘fixturize’ pones un array de modelos, entonces sólo esos modelos se auto-simularán mientras que los demás utilizarán las tablas reales. Si quieres usar tus archivos de fixtures con testAction() no uses fixturize, y en su lugar usa las fixtures como harías normalmente.
method
Ajustalo a ‘post’ o ‘get’ si quieres pasarle datos al controlador
data
Los datos que se pasarán. Será un array asociativo consistente en pares de campo => valor. Échale un vistazo a function testIndexPostFixturized() en el case test de arriba para ver cómo emulamos pasar datos de formulario como post para un nuevo artículo.

Pitfalls

If you use testAction to test a method in a controller that does a redirect, your test will terminate immediately, not yielding any results.

http://mark-story.com/posts/view/testing-cakephp-controllers-the-hard-way for a possible fix.

Testing Helpers

Since a decent amount of logic resides in Helper classes, it’s important to make sure those classes are covered by test cases.

Helper testing is a bit similar to the same approach for Components. Suppose we have a helper called CurrencyRendererHelper located in app/views/helpers/currency_renderer.php with its accompanying test case file located in app/tests/cases/helpers/currency_renderer.test.php

Creating Helper test, part I

First of all we will define the responsibilities of our CurrencyRendererHelper. Basically, it will have two methods just for demonstration purpose:

function usd($amount)

This function will receive the amount to render. It will take 2 decimal digits filling empty space with zeros and prefix ‘USD’.

function euro($amount)

This function will do the same as usd() but prefix the output with ‘EUR’. Just to make it a bit more complex, we will also wrap the result in span tags:

<span class="euro"></span>

Let’s create the tests first:

<?php

//Import the helper to be tested.
//If the tested helper were using some other helper, like Html,
//it should be impoorted in this line, and instantialized in startTest().
App::import('Helper', 'CurrencyRenderer');

class CurrencyRendererTest extends CakeTestCase {
    private $currencyRenderer = null;

    //Here we instantiate our helper, and all other helpers we need.
    public function startTest() {
        $this->currencyRenderer = new CurrencyRendererHelper();
    }

    //testing usd() function.
    public function testUsd() {
        $this->assertEqual('USD 5.30', $this->currencyRenderer->usd(5.30));
        //We should always have 2 decimal digits.
        $this->assertEqual('USD 1.00', $this->currencyRenderer->usd(1));
        $this->assertEqual('USD 2.05', $this->currencyRenderer->usd(2.05));
        //Testing the thousands separator
        $this->assertEqual('USD 12,000.70', $this->currencyRenderer->usd(12000.70));
    }
}

Here, we call usd() with different parameters and tell the test suite to check if the returned values are equal to what is expected.

Executing the test now will result in errors (because currencyRendererHelper doesn’t even exist yet) showing that we have 3 fails.

Once we know what our method should do, we can write the method itself:

<?php
class CurrencyRendererHelper extends AppHelper {
    public function usd($amount) {
        return 'USD ' . number_format($amount, 2, '.', ',');
    }
}

Here we set the decimal places to 2, decimal separator to dot, thousands separator to comma, and prefix the formatted number with ‘USD’ string.

Save this in app/views/helpers/currency_renderer.php and execute the test. You should see a green bar and messaging indicating 4 passes.

Probando componentes

Supongamos que queremos hacer test a un componente llamado TransporterComponent, el cual usa un modelo llamado Transporter para proporcionar funcionalidad a otros controladores. Utilizaremos cuatro archivos:

  • Un componente llamado Transporters que se encuentra en app/controllers/components/transporter.php
  • Un modelo llamado Transporte que está en app/models/transporter.php
  • Una fixture llamada TransporterTestFixture situada en app/tests/fixtures/transporter_fixture.php
  • El código para el test, en app/tests/cases/transporter.test.php

Initializing the component

Ya que CakePHP desaliante importar modelos directamente en los componentes necesitamos un controlador para acceder a los datos en el mmodelo.

Si el método startup() del componente tiene este aspecto:

public function startup(&$controller){
          $this->Transporter = $controller->Transporter;
 }

entonces podemos simplemente crear una clase sencilla:

class FakeTransporterController {}

y asignarle valores dentro de ella como aquí:

$this->TransporterComponentTest = new TransporterComponent();
$controller = new FakeTransporterController();
$controller->Transporter = new TransporterTest();
$this->TransporterComponentTest->startup(&$controller);

Creando un método de prueba

Simplemente crea una clase que extienda CakeTestCase y ¡comienza a escribir tests!

class TransporterTestCase extends CakeTestCase {
    var $fixtures = array('transporter');
    function testGetTransporter() {
          $this->TransporterComponentTest = new TransporterComponent();
          $controller = new FakeTransporterController();
          $controller->Transporter = new TransporterTest();
          $this->TransporterComponentTest->startup(&$controller);

          $result = $this->TransporterComponentTest->getTransporter("12345", "Sweden", "54321", "Sweden");
          $this->assertEqual($result, 1, "SP is best for 1xxxx-5xxxx");

          $result = $this->TransporterComponentTest->getTransporter("41234", "Sweden", "44321", "Sweden");
          $this->assertEqual($result, 2, "WSTS is best for 41xxx-44xxx");

          $result = $this->TransporterComponentTest->getTransporter("41001", "Sweden", "41870", "Sweden");
          $this->assertEqual($result, 3, "GL is best for 410xx-419xx");

          $result = $this->TransporterComponentTest->getTransporter("12345", "Sweden", "54321", "Norway");
          $this->assertEqual($result, 0, "Noone can service Norway");
   }
}

Web testing - Testeando las vistas

La mayoria, si no es que lo son todos, los proyectos CakePHP son aplicaciones web. Aunque el testeo unitario es una excelente manera de testear pequeñas porciones de nuestro código, hay ocaciones en la que querriamos hacer un testeo a gran escala. La clase CakeWebTestCase nos brinda una muy buena manera de hacer éste tipo de testing, desde el punto de vista del usuario.

About CakeWebTestCase

CakeWebTestCase es una extensión directa de SimpleTest WebTestCase, sin ninguna funcionalidad extra. Toda la funcionalidad encontrada en la documentación de SimpleTest para Testeo Web (Web testing) tambien están disponibles aqui. Esto quiere decir que no se pueden usar los fixtures, y que todos los casos de testeo involucrados en un ABM (alta, baja o modificación) a la base de datos modificarán permanentemente los valores. Los resultados del Test son comparados frecuentemente con los qe tiene la base de datos, por lo tanto, asegurarse que la bd tenga los valores que se esperan, es parte del proceso de construcción del test.

Creando un test

Manteniendo las convenciones de los otros tests, los archivos de testeo de vistas se deberán crear en la carpeta tests/cases/views. Claro que se podrian guardar en otra ubicación, pero siempre es bueno seguir las convenciones. Entonces, crearemos el archivo: tests/cases/views/complete_web.test.php

Para escribir testeos web, deberás extender la clase CakeWebTestCase y no CakeTestCase, tal como era en los otros tests:

class CompleteWebTestCase extends CakeWebTestCase

Si necesitas hacer alguna inicialización antes de que comience el test, crea el constructor:

function CompleteWebTestCase(){
  //Do stuff here
}

Cuando escribes los test cases, lo primero que vas a necesitar hacer es capturar algun tipo de salida o resultado donde ver y analizar. Ésto puede ser realizado haciendo un request get o post, usando los métodos get()o post() respectivamente. A ambos métodos se le pasa como primer parámetro la url, aunque puede ser traida dinámicamente si asumimos que script de testing está en http://your.domain/cake/folder/webroot/test.php tipeando:

$this->baseurl = current(split("webroot", $_SERVER['PHP_SELF']));

Entonces podremos hacer gets y posts usando las urls de Cake, por ejemplo:

$this->get($this->baseurl."/products/index/");
$this->post($this->baseurl."/customers/login", $data);

El segundo parámetro del método post, $data, es un array asociativo que contiene post data en el formato de Cake:

$data = array(
  "data[Customer][mail]" => "[email protected]",
  "data[Customer][password]" => "userpass");

Una vez que se hizo el request a la página, se pueden utilizar todos los mismos asserts que veniamos usando en SimpleTest.

Walking through a page

CakeWebTest also gives you an option to navigate through your page by clicking links or images, filling forms and clicking buttons. Please refer to the SimpleTest documentation for more information on that.

Testing plugins

Tests for plugins are created in their own directory inside the plugins folder.

/app
     /plugins
         /pizza
             /tests
                  /cases
                  /fixtures
                  /groups

They work just like normal tests but you have to remember to use the naming conventions for plugins when importing classes. This is an example of a testcase for the PizzaOrder model from the plugins chapter of this manual. A difference from other tests is in the first line where ‘Pizza.PizzaOrder’ is imported. You also need to prefix your plugin fixtures with ‘plugin.plugin_name.‘.

<?php
App::import('Model', 'Pizza.PizzaOrder');

class PizzaOrderCase extends CakeTestCase {

    // Plugin fixtures located in /app/plugins/pizza/tests/fixtures/
    var $fixtures = array('plugin.pizza.pizza_order');
    var $PizzaOrderTest;

    function testSomething() {
        // ClassRegistry makes the model use the test database connection
        $this->PizzaOrderTest =& ClassRegistry::init('PizzaOrder');

        // do some useful test here
        $this->assertTrue(is_object($this->PizzaOrderTest));
    }
}
?>

If you want to use plugin fixtures in the app tests you can reference them using ‘plugin.pluginName.fixtureName’ syntax in the $fixtures array.

That is all there is to it.

Miscellaneous

Customizing the test reporter

The standard test reporter is very minimalistic. If you want more shiny output to impress someone, fear not, it is actually very easy to extend. By creating a new reporter and making a request with a matching output GET parameter you can get test results with a custom reporter.

Reporters generate the visible output from the test suite. There are two built in reporters: Text and Html. By default all web requests use the Html reporter. You can create your own reporters by creating files in your app/libs. For example you could create the file app/libs/test_suite/reporters/my_reporter.php and in it create the following:

require_once CAKE_TEST_LIB . 'reporter' . DS . 'cake_base_reporter.php';

class MyReporter extends CakeBaseReporter {
    //methods go here.
}

Extending CakeBaseReporter or one of its subclasses is not required, but strongly suggested as you may get missing errors otherwise. CakeBaseReporter encapsulates a few common test suite features such as test case timing and code coverage report generation. You can use your custom reporter by setting the output query string parameter to the reporter name minus ‘reporter’. For the example above you would set output=my to use your custom reporter.

Test Reporter methods

Reporters have a number of methods used to generate the various parts of a Test suite response.

paintDocumentStart()
Paints the start of the response from the test suite. Used to paint things like head elements in an html page.
paintTestMenu()
Paints a menu of available test cases.
testCaseList()
Retrieves and paints the list of tests cases.
groupCaseList()
Retrieves and paints the list of group tests.
paintHeader()
Prints before the test case/group test is started.
paintPass()
Prints everytime a test case has passed. Use $this->getTestList() to get an array of information pertaining to the test, and $message to get the test result. Remember to call parent::paintPass($message).
paintFail()
Prints everytime a test case has failed. Remember to call parent::paintFail($message).
paintSkip()
Prints everytime a test case has been skipped. Remember to call parent::paintSkip($message).
paintException()
Prints everytime there is an uncaught exception. Remember to call parent::paintException($message). Prints everytime an error is raised. Remember to call parent::paintError($message).
paintFooter()
Prints when the test case/group test is over, i.e. when all test cases has been executed.
paintDocumentEnd()
Paints the end of the response from the test suite. Used to paint things like footer elements in an html page.

Grouping tests

If you want several of your test to run at the same time, you can try creating a test group. Create a file in /app/tests/groups/ and name it something like your_test_group_name.group.php. In this file, extend TestSuite and import test as follows:

<?php
class TryGroupTest extends TestSuite {
  var $label = 'try';
  function tryGroupTest() {
    TestManager::addTestCasesFromDirectory($this, APP_TEST_CASES . DS . 'models');
  }
}
?>

The code above will group all test cases found in the /app/tests/cases/models/ folder. To add an individual file, use TestManager::addTestFile($this, filename).

Running tests in the Command Line

If you have simpletest installed you can run your tests from the command line of your application.

from app/

cake testsuite help
Usage:
    cake testsuite category test_type file
        - category - "app", "core" or name of a plugin
        - test_type - "case", "group" or "all"
        - test_file - file name with folder prefix and without the (test|group).php suffix

Examples:
        cake testsuite app all
        cake testsuite core all

        cake testsuite app case behaviors/debuggable
        cake testsuite app case models/my_model
        cake testsuite app case controllers/my_controller

        cake testsuite core case file
        cake testsuite core case router
        cake testsuite core case set

        cake testsuite app group mygroup
        cake testsuite core group acl
        cake testsuite core group socket

        cake testsuite bugs case models/bug
          // for the plugin 'bugs' and its test case 'models/bug'
        cake testsuite bugs group bug
          // for the plugin bugs and its test group 'bug'

Code Coverage Analysis:


Append 'cov' to any of the above in order to enable code coverage analysis

As the help menu suggests, you’ll be able to run all, part, or just a single test case from your app, plugin, or core, right from the command line.

If you have a model test of test/models/my_model.test.php you’d run just that test case by running:

cake testsuite app models/my_model

Test Suite changes in 1.3

The TestSuite harness for 1.3 was heavily refactored and partially rebuilt. The number of constants and global functions have been greatly reduced. Also the number of classes used by the test suite has been reduced and refactored. You must update app/webroot/test.php to continue using the test suite. We hope that this will be the last time that a change is required to app/webroot/test.php.

Removed Constants

  • CAKE_TEST_OUTPUT
  • RUN_TEST_LINK
  • BASE
  • CAKE_TEST_OUTPUT_TEXT
  • CAKE_TEST_OUTPUT_HTML

These constants have all been replaced with instance variables on the reporters and the ability to switch reporters.

Removed functions

  • CakePHPTestHeader()
  • CakePHPTestSuiteHeader()
  • CakePHPTestSuiteFooter()
  • CakeTestsGetReporter()
  • CakePHPTestRunMore()
  • CakePHPTestAnalyzeCodeCoverage()
  • CakePHPTestGroupTestList()
  • CakePHPTestCaseList()

These methods and the logic they contained have been refactored/rewritten into CakeTestSuiteDispatcher and the relevant reporter classes. This made the test suite more modular and easier to extend.

Removed Classes

  • HtmlTestManager
  • TextTestManager
  • CliTestManager

These classes became obsolete as logic was consolidated into the reporter classes.

Modified methods/classes

The following methods have been changed as noted.

  • TestManager::getExtension() is no longer static.
  • TestManager::runAllTests() is no longer static.
  • TestManager::runGroupTest() is no longer static.
  • TestManager::runTestCase() is no longer static.
  • TestManager::getTestCaseList() is no longer static.
  • TestManager::getGroupTestList() is no longer static.

testsuite Console changes

The output of errors, exceptions, and failures from the testsuite console tool have been updated to remove redundant information and increase readability of the messages. If you have other tools built upon the testsuite console, be sure to update those tools with the new formatting.

CodeCoverageManager changes

  • CodeCoverageManager::start()‘s functionality has been moved to CodeCoverageManager::init()
  • CodeCoverageManager::start() now starts coverage generation.
  • CodeCoverageManager::stop() pauses collection
  • CodeCoverageManager::clear() stops and clears collected coverage reports.