Page Contents
- Testing
This document is for a version of CakePHP that is no longer supported. Please upgrade to a newer release!
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.
¿Preparado/a para empezar a hacer test? ¡Bien! ¡Vamos allá entonces!
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í: https://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.
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!
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.
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:
Crea tablas para cada una de las fixtures necesarias
Rellena las tablas con datos, si es que se han proporcionado éstos en la fixture
Ejecuta los métodos de los test
Vacía las tablas fixture
Elimina las tablas fixture de la base de datos
Cuando se crea un fixture se deben definir 2 cosas:
cómo se crea la tabla (que campos serán parte de la tabla)
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:
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)
setea el campo como primary para hacerlo auto-incrementable (AUTO_INCREMENT), y clave primaria (PRIMARY KEY) de la tabla.
setea el tamaño del campo.
setea true o false. Si puede ser nulo indicamos true, si no se permiten nulos va false
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).
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')
);
}
?>
En primer lugar, revisar una serie de normas y directrices para los tests:
Los archivos de PHP que contiene los tests deben estar en : app/tests/cases/[algun_ archivo].
Los nombres de estos archivos deben terminar con un .test.php en lugar de sólo .php.
Las clases que contienen los tests debe extender o heredar de CakeTestCase o CakeWebTestCase.
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 https://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.
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:
First method called in a test case.
Last method called in a test case.
called before a test case is started.
called after a test case has run.
Announces the start of a test method.
Announces the end of a test method.
Called just before a test method is executed.
Called just after a test method has completed.
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) {
$conditions = array(
$this->name . '.published' => 1
);
return $this->findAll($conditions, $fields);
}
}
?>
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 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 DB config if they rely on fixtures in your testcase!
Since we also want to reuse all our existing model code we will create 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.
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);
}
}
?>
You can see we have added a method called testPublished(). We start 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.
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);
}
}
?>
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:
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”.
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.
Ajustalo a “post” o “get” si quieres pasarle datos al controlador
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.
If you use testAction to test a method in a controller that does a redirect, your test will terminate immediately, not yielding any results. See https://trac.cakephp.org/ticket/4154 for a possible fix.
For an in-depth explanation of controller testing please see this blog post by Mark Story Testing CakePHP Controllers the hard way.
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
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.
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
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);
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");
}
}
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.
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.
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.
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.
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.
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. The only danger is that you have to fiddle with core Cake code, specifically /cake/tests/libs/cake_reporter.php.
To change the test output you can override the following methods:
Prints before the test is started.
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).
Prints everytime a test case has failed. Remember to call parent::paintFail($message).
Prints when the test is over, i.e. when all test cases has been executed.
If, when running paintPass and paintFail, you want to hide the parent output, enclose the call in html comment tags, as in:
echo "\n<!-- ";
parent::paintFail($message);
echo " -->\n";
A sample cake_reporter.phpsetup that creates a table to hold the test results follows:
<?php
/**
* CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite>
* Copyright 2005-2008, Cake Software Foundation, Inc.
* 1785 E. Sahara Avenue, Suite 490-204
* Las Vegas, Nevada 89104
*
* Licensed under The Open Group Test Suite License
* Redistributions of files must retain the above copyright notice.
*/
class CakeHtmlReporter extends HtmlReporter {
function CakeHtmlReporter($characterSet = 'UTF-8') {
parent::HtmlReporter($characterSet);
}
function paintHeader($testName) {
$this->sendNoCacheHeaders();
$baseUrl = BASE;
print "<h2>$testName</h2>\n";
print "<table style=\"\"><th>Res.</th><th>Test case</th><th>Message</th>\n";
flush();
}
function paintFooter($testName) {
$colour = ($this->getFailCount() + $this->getExceptionCount() > 0 ? "red" : "green");
print "</table>\n";
print "<div style=\"";
print "padding: 8px; margin-top: 1em; background-color: $colour; color: white;";
print "\">";
print $this->getTestCaseProgress() . "/" . $this->getTestCaseCount();
print " test cases complete:\n";
print "<strong>" . $this->getPassCount() . "</strong> passes, ";
print "<strong>" . $this->getFailCount() . "</strong> fails and ";
print "<strong>" . $this->getExceptionCount() . "</strong> exceptions.";
print "</div>\n";
}
function paintPass($message) {
parent::paintPass($message);
echo "<tr>\n\t<td width=\"20\" style=\"border: dotted 1px; border-top: hidden; border-left: hidden; border-right: hidden\">\n";
print "\t\t<span style=\"color: green;\">Pass</span>: \n";
echo "\t</td>\n\t<td width=\"40%\" style=\"border: dotted 1px; border-top: hidden; border-left: hidden; border-right: hidden\">\n";
$breadcrumb = $this->getTestList();
array_shift($breadcrumb);
array_shift($breadcrumb);
print implode("->", $breadcrumb);
echo "\n\t</td>\n\t<td width=\"40%\" style=\"border: dotted 1px; border-top: hidden; border-left: hidden; border-right: hidden\">\n";
$message = split('at \[', $message);
print "->$message[0]<br />\n\n";
echo "\n\t</td>\n</tr>\n\n";
}
function paintFail($message) {
echo "\n<!-- ";
parent::paintFail($message);
echo " -->\n";
echo "<tr>\n\t<td width=\"20\" style=\"border: dotted 1px; border-top: hidden; border-left: hidden; border-right: hidden\">\n";
print "\t\t<span style=\"color: red;\">Fail</span>: \n";
echo "\n\t</td>\n\t<td width=\"40%\" style=\"border: dotted 1px; border-top: hidden; border-left: hidden; border-right: hidden\">\n";
$breadcrumb = $this->getTestList();
print implode("->", $breadcrumb);
echo "\n\t</td>\n\t<td width=\"40%\" style=\"border: dotted 1px; border-top: hidden; border-left: hidden; border-right: hidden\">\n";
print "$message";
echo "\n\t</td>\n</tr>\n\n";
}
function _getCss() {
return parent::_getCss() . ' .pass { color: green; }';
}
}
?>
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 GroupTest and import test as follows:
<?php
class TryGroupTest extends GroupTest {
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).
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 case models/my_model