This document is for a version of CakePHP that is no longer supported. Please upgrade to a newer release!
Mit CakePHP 1.2_RC2 wurde eine umfangreiche Test-Umgebung integriert. Diese Test-Umgebung ist eine Ergänzung bzw. Erweiterung zur bisherigen SimpleTest-Umgebung für PHP. Dieser Abschnitt befasst sich damit, wie man Tests vorbereitet, aufbaut und ausführt.
Fertig zum Beginn der Tests? Optimal! Dann lass uns endlich anfangen! :-)
Das in CakePHP 1.2 enthaltene Test-Framework ist aufgebaut auf dem SimpleTest-Test-Framework. SimpleTest ist in der Standard-CakePHP-Installation nicht enthalten, somit müssen wir das nun runterladen. Hier findest du SimpleTest: https://simpletest.sourceforge.net/. Besorg dir die letzte Version und entpack die Dateien entweder in deinen /cake/vendors/ oder in den /app/vendors/ Ordner, je nach euren Anforderungen. Du solltest also jetzt einen ../vendors/simpletest/ Ordner samt der dazugehörigen SimpleTest-Dateien haben!? Vergiss nicht, dass du in deiner app/config/core.php das „DEBUG-level“ mindestens auf 1 setzt, bevor du mit irgendwelchen Test`s anfängst!
CakePHP 1.2 kommt mit einem Bündel an Test-Fallbeispielen, involviert in die Core-CakePHP-Funktionen, daher. Auf diese Test`s kann zugegriffen werden indem man mit dem Browser https://deine.cake.adresse/dein_cake_ordner/test.php (je nachdem wie euer spezifisches Setup aussieht..), ansteuert. Probier eine von den Core-Test-Gruppen aus (indem du die Links anklickst… ;-D). Das Ausführen der Test-Gruppen könnte möglicherweise ein Weilchen dauern, aber schlussendlich sollte etwas wie: „2/2 test casese complete: 49 passes, 0 fails and 0 exceptions.“ angezeigt werden. Gratuliere, du bist jetzt bereit um Schreib-Test`s zu machen!
Das CakePHP-Test-Framework unterstützt 2 Arten von Tests. Die eine ist der Unit-Test bzw. Einzel-Test, mit welchem du kleine Teile deines Codes testen kannst, wie eine „Methode in einer Aktion“ oder eine „Aktion in einem Controller“. Die andere unterstützte Test-Art ist der Web-Test, mit dem du die Arbeit der Tests, durch Seiten-Navigation, Formulare ausfüllen, Links anklicken u.s.w, automatisieren kannst.
Wenn man Code testet der auf models und Daten basiert, dann kann man für eines davon *fixtures* nutzen, als einen Weg um temporäre Datensätze mit Beispieldaten zu laden und diese dann zum testen zu verwenden.
Der Vorteil beim verwenden von fixtures liegt darin, dass dein Test keine Chance hat geladene Anwendungsdaten zu zerstören. Zusätzlich kannst du damit Anfangen deine Code-Priorität, mit aktuell gefertigten Live-Inhalten für eine Anwendung, zu testen.
CakePHP versucht die Verbindung namens $test
in deiner
/app/config/database.php Einstellungs-Datei zu benutzen. Wenn diese
Verbindung nicht brauchbar ist, dann wird die $default
Datenbank-Verbindung genutzt und die Test-Tabellen werden in der dort
definierten Datenbank erstellt.
In einem anderen Fall, wird „test_suite“ zu deinem eigenen Tabellen-Zusatz(falls vorhanden) hinzu gefügt um einer Kollision, mit einer vorhandenen Tabelle, vorzubeugen.
CakePHP bietet folgende Möglichkeiten wärend der Tour durch fixtures-basierte Test Fälle:
Erstellt Tabellen für alles was von den fixtures gebraucht wird
Füllt Tabellen mit Daten, wenn Daten in fixture angefordert werden
Startet Test Methoden
Entleert fixture-Tabellen
Löscht fixture-Tabellen aus der Datenbank
Beim Erstellen von Vorrichtungen solltest du hauptsächlich 2 Dinge definieren: Wie wurde die Tabelle erstellt (welche Felder sind ein Teil der Tabelle) und welche Datensätze werden zu Beginn die Test-Tabelle belegen. Dann lass uns mal die erste Vorrichtung (fixture) erstellen, womit wir dann unsere Modell-Artikel testen. Erstelle eine Datei mit dem Namen article_test_fixture.php in deinem ../app/tests/fixtures/ Ordner, mit folgendem Inhalt:
<?php
class ArticleTestFixture extends CakeTestFixture {
var $name = 'ArticleTest';
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')
);
}
?>
Wir brauchen $fields um zu spezifizieren welche Felder Bestandteil der Tabelle sind und wie diese Felder definiert sind. Das Format das gebraucht wird, um diese Felder zu definieren, ist dass gleiche das in der Funktion generateColumnSchema(), aus der Cake-Datenbank-Engineklasse, definiert wird (zum Beispiel die Datei dbo_mysql.php). Lass uns mal schauen welche Attribute ein Feld haben kann und was diese aussagen:
CakePHP`s interner Daten-Typ. Momentan unterstützt: string (Speicherort für diverse Zeichenketten/-folgen), text (Speicherort für Texte), integer (Speicherort für Ganzzahlen), float (Speicherort für Fliesskommazahlen), datetime (Speicherort für Datum+Zeit), timestamp (Speicherort für Zeitmarken), time (Speicherort für Zeiten), date (Speicherort für Datumsangaben), und binary (Speicherort für Binärzahlen/zeichen)
auf primary setzen damit das Feld AUTO-ANWACHSEN lassen zu können und um einen Hauptschlüssel für die Tabelle zu erstellen.
setzen um dem Feld die spezielle länge zu geben die es haben sollte.
Setze den Wert auf „true“ („wahr“ => Zum Erlauben von NULLen) oder „false“ („unwahr“ => um NULLen zu verbieten)
Standart-Einheit des Feldes..
Zum Schluss könnten wir eine Reihe von Datensätzen setzen, welche publiziert werden nach dem die Test-Tabelle erstellt wurde. Das Format ist ziemlich ordentlich und braucht wenig weitere Erklärung. Versuch daran zu denken, dass jeder Datensatz im $records Ordner einen Schlüssel haben muss für jedes Feld das im $fields Ordner angegeben ist. Falls ein Feld für für einen speziellen Datensatz einen NULL-Wert braucht, dann deklariere den Wert des Schlüssel`s als NULL.
Deine Anwendung mag möglicherweise funktionierende Entwürfe beinhalten, mit echten untereinander verknüpften Daten, und du könntest dich dazu entscheiden deine Entwürfe mit diesen Daten zu testen… Das wäre wohl dann eine Doppelanstrengung, wenn man die Tabellendefinition festlegen und/oder die Datensätze mitsamt deinen Inhalten definieren sollte. Glücklicherweise, gibt es da`die Möglichkeit zum festlegen der Tabellendefinition und/oder den Datensätzen für bestimmte Inhalte, die kommen von fertigen Modellen oder einer bereits vorhandenen Tabelle. Dann lass uns mal eine Musteraufgabe anfangen. Mal angenommen du hast ein Model mit dem Namen Article in deiner Anwendung verfügbar (das weist auf die Tabelle articles hin!), ändere die Muster-Inhalte aus der vorherigen Sektion (app/tests/fixtures/article_test_fixture.php) wie folgt:
<?php
class ArticleTestFixture extends CakeTestFixture {
var $name = 'ArticleTest';
var $import = 'Article';
}
?>
Die og Anweisungen weisen die Testfolge an, deine Tabellendefinition aus der Tabelle, welche im Model Article verlinkt ist, zu importieren. Du kannst dazu jegliches Muster verwenden, dass du in deiner Anwendung verfügbar ist. Oben die Anweisung importiert keinerlei Datensätze, dies kannst du dann tun wenn du die Anweisung umänderst zu:
<?php
class ArticleTestFixture extends CakeTestFixture {
var $name = 'ArticleTest';
var $import = array('model' => 'Article', 'records' => true);
}
?>
Wenn du andererseits eine Tabelle erstellt hast, aber keine Muster-Inhalte dazu vorhanden sind, dann kannst du anweisen dass ein import veranlasst wird, indem anstatt der Model-Daten dessen Tabellen-Informationen ausgelesen werden. Zum Bsp:
<?php
class ArticleTestFixture extends CakeTestFixture {
var $name = 'ArticleTest';
var $import = array('table' => 'articles');
}
?>
Der obere Code-Fetzen wird einen import der Tabellendefinition von der Tabelle articles veranlassen, indem die CakePHP-Datenbankverbindung ‚default‘ genutzt wird. Wenn du die aktuell verwendete Datenbank-Verbindung ändern möchtest, musst du folgende Zeilen ändern:
<?php
class ArticleTestFixture extends CakeTestFixture {
var $name = 'ArticleTest';
var $import = array('table' => 'articles', 'connection' => 'other');
}
?>
Seit es deine CakePHP Datenbankverbindung verwendet hat wird es, wenn da irgendwelche Tabellen-Vorzeichen erkennbar sind, automatisch gebraucht, da attraktive Tabellen-Informationen automatisch verwendet werden. Die zwei Code-Schnipsel da oben importieren keine Datensätze aus der Tabelle. Um also die Inhalte zum import der Datensätze zu zwingen, ädere folgende Dinge:
<?php
class ArticleTestFixture extends CakeTestFixture {
var $name = 'ArticleTest';
var $import = array('table' => 'articles', 'records' => true);
}
?>
Du kannst natürlich auch deine Tabellendefinition aus einem exitierenden Muster oder einer Tabelle importieren, aber halte deine Datensätze direkt definiert, genau nach den Vorgaben aus der vorhergehenden Sektion. zum Beispiel:
<?php
class ArticleTestFixture extends CakeTestFixture {
var $name = 'ArticleTest';
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')
);
}
?>
Zunächst erstmal eine Latte von Regeln oder Richtlinien bezüglich der Test`s:
PHP Dateien die Test`s beinhalten, sollten im app/tests/cases/[some_folder] Ordner sein.
Die Dateinamen dieser Dateien sollten am Ende etwa so aussehen:.test.php anstatt sowas: .php.
Die Klassen die die Test enthalten, müssen CakeTestCase oder CakeWebTestCase ausführen (extend).
Der Name von einigen Methoden die ebenfalls Tests enthalten können (d.h. enthält eine Erklärung) sollten dann mit test beginnen, wie zum Beispiel: testPublished().
Wenn du einen Test-Fall erstellt hast, dann kannst du diesen starten, indem du mit deinem Browser folgende Adresse ansteuerst: https://deine.cake.domain/cake_ordner/test.php (abhängig von deinem persönlichen Setup für CakePHP!). Im Anschluss an`s durchklicken der Programm-Test-Möglichkeiten, bitte den Link zu deiner persönlichen Datei anklicken.
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.
Lass uns darauf einigen dass wir unseren Muster-Artikel (article model) bereits unter ../app/models/article.php erstellt haben und dieser sollte in etwa wie folgt aussehen:
<?php
class Article extends AppModel {
var $name = 'Article';
function published($fields = null) {
$conditions = array(
$this->name . '.published' => 1
);
return $this->findAll($conditions, $fields);
}
}
?>
Wir möchten jetzt einen Versuch aufbauen welcher Beispielmodule verwenden wird, allerdings durch Vorrichtungen für Versuche, lässt sich einiges an Funktionalität im Versuch testen. Die CakePHP Versuchs-Umgebung läd nur einen sehr kleinen Teil der Anwendungen (um Versuche isoliert zu lassen), somit müssen wir zum starten das vorhergehende Module verwenden (in diesem Fall ist das Anwendungsmodul ja schon fertig definiert), dann informiere die Versuchs-Umgebung darüber, dass wir das Modul testen wollen, indem wir herausfinden welche Datenbank-Konfiguration benutzt werden sollte!. Die CakePHP Test-Umgebung ermöglicht eine Datenbank-Konfiguration namen`s test_suite, diese wird gebraucht für alle Module, die auf Vorrichtungen angewiesen sind. Der Datensatz $useDbConfig zu dieser Konfigurationsdatei lässt CakePHP wissen das dieses Modul die test_suite DB-Verbindung benutzt. Seit wir darüber nachdenken all unsere Anwendungen nochmal wiederzuverwerten umd das ganze dazu benutzen das wir alle want to reuse all our existing modules 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 ArticleTest extends Article {
var $name = 'ArticleTest';
var $useDbConfig = 'test_suite';
}
class ArticleTestCase extends CakeTestCase {
var $fixtures = array( 'app.article_test' );
}
?>
Wie du erkennen solltest we’re not really adding any test methods yet, we have just defined our ArticleTest model (that inherits from Article), and created the ArticleTestCase. In variable $fixtures we define the set of fixtures that we’ll use.
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.
Say you have a typical articles controller, with its corresponding model, and it looks like this:
<?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);
}
}
?>
Create a file named articles_controller.test.php in your app/tests/cases/controllers directory and put the following inside:
<?php
class ArticlesControllerTest extends CakeTestCase {
function startCase() {
echo '<h1>Starting Test Case</h1>';
}
function endCase() {
echo '<h1>Ending Test Case</h1>';
}
function startTest($method) {
echo '<h3>Starting method ' . $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);
}
}
?>
The new thing here is the testAction method. The first argument of that method is the Cake url of the controller action to be tested, as in ‚/articles/index/short‘.
The second argument is an array of parameters, consisting of:
Valid values are:
‚vars‘ - You get the view vars available after executing action
‚view‘ - You get The rendered view, without the layout
‚contents‘ - You get the rendered view’s complete html, including the layout
‚result‘ - You get the returned value when action uses $this->params[‚requested‘].
The default is ‚result‘.
Set to true if you want your models auto-fixturized (so your application tables get copied, along with their records, to test tables so if you change data it does not affect your real application.) If you set ‚fixturize‘ to an array of models, then only those models will be auto-fixturized while the other will remain with live tables. If you wish to use your fixture files with testAction() do not use fixturize, and instead just use fixtures as you normally would.
set to ‚post‘ or ‚get‘ if you want to pass data to the controller
the data to be passed. Set it to be an associative array consisting
of fields => value. Take a look at
function testIndexPostFixturized()
in above test case to see how
we emulate posting form data for a new article submission.
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.
Lets assume that we want to test a component called TransporterComponent, which uses a model called Transporter to provide functionality for other controllers. We will use four files:
A component called Transporters found in app/controllers/components/transporter.php
A model called Transporter found in app/models/transporter.php
A fixture called TransporterTestFixture found in app/tests/fixtures/transporter_fixture.php
The testing code found in app/tests/cases/transporter.test.php
Since CakePHP discourages from importing models directly into components we need a controller to access the data in the model.
If the startup() function of the component looks like this:
public function startup(&$controller){
$this->Transporter = $controller->Transporter;
}
then we can just design a really simple fake class:
class FakeTransporterController {}
and assign values into it like this:
$this->TransporterComponentTest = new TransporterComponent();
$controller = new FakeTransporterController();
$controller->Transporter = new TransporterTest();
$this->TransporterComponentTest->startup(&$controller);
Just create a class that extends CakeTestCase and start writing 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");
}
}
Most, if not all, CakePHP projects result in a web application. While unit tests are an excellent way to test small parts of functionality, you might also want to test the functionality on a large scale. The CakeWebTestCase class provides a good way of doing this testing from a user point-of-view.
CakeWebTestCase is a direct extension of the SimpleTest WebTestCase, without any extra functionality. All the functionality found in the SimpleTest documentation for Web testing is also available here. This also means that no functionality other than that of SimpleTest is available. This means that you cannot use fixtures, and all web test cases involving updating/saving to the database will permanently change your database values. Test results are often based on what values the database holds, so making sure the database contains the values you expect is part of the testing procedure.
In keeping with the other testing conventions, you should create your view tests in tests/cases/views. You can, of course, put those tests anywhere but following the conventions whenever possible is always a good idea. So let’s create the file tests/cases/views/complete_web.test.php
First, when you want to write web tests, you must remember to extend CakeWebTestCase instead of CakeTestCase:
class CompleteWebTestCase extends CakeWebTestCase
If you need to do some preparation before you start the test, create a constructor:
function CompleteWebTestCase(){
//Do stuff here
}
When writing the actual test cases, the first thing you need to do is get some output to look at. This can be done by doing a get or post request, using get()or post() respectively. Both these methods take a full url as the first parameter. This can be dynamically fetched if we assume that the test script is located under http://your.domain/cake/folder/webroot/test.php by typing:
$this->baseurl = current(split("webroot", $_SERVER['PHP_SELF']));
You can then do gets and posts using Cake urls, like this:
$this->get($this->baseurl."/products/index/");
$this->post($this->baseurl."/customers/login", $data);
The second parameter to the post method, $data, is an associative array containing the post data in Cake format:
$data = array(
"data[Customer][mail]" => "[email protected]",
"data[Customer][password]" => "userpass");
When you have requested the page you can do all sorts of asserts on it, using standard SimpleTest web test methods.
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