Автор Владимир Лучанинов
Мне понравилось тестировать как можно больше всего в CakePHP. О том как тестировать компоненты, я уже писал, теперь пришло время моделей.
В поисках подходящего the fucking manual я набрёл на Testing Models with CakePHP 1.2 test suite на bakery.cakephp.org.
И всё очень хорошо расписано, но, блин, не работает и всё. Поэтому не читайте то, что там написано. Я тщательно изучив исходники методом научного тыка сделал так, чтобы всё работало.
Модели работают с базой данных и соответственно надо проверить насколько хорошо это получается. Я сторонник fat Models, thin Controllers и стараюсь переносить всю логику работы с данными в модели. Поэтому проверять надо много.
Можно указать отдельное подключение для тестовой базы данных в app/config/database.php в переменной $test. Обычно подходят те же значения, что и в $default. Если вы уберёте $test, то Cake PHP? сам установит её такой же как и $default. В любом случае, не волнуйтесь, потому что для тестовых таблиц будет добавлен префикс test_suite.
Для создания тестовых таблиц используются fixtures. Они создают тестовые таблицы до проведения теста, перед каждым тестом загружают туда тестовые данные, после каждого теста очищают тестовые таблицы и после последнего теста удаляют их.
Как говорится «сначала было слово», вот мы и создадим fixture для модели Word.
app/test/fixtures/word_test_fixture.php
<?
class WordTestFixture extends CakeTestFixture {
var $name = 'WordTest';
var $useTable = 'words';
var $import = 'Word';
var $records = array(
array('id'=>1, 'name'=>'Table'),
array('id'=>2, 'name'=>'boy'),
array('id'=>3, 'name'=>'girl'),
);
}
?>
Эта fixture создаст таблицу test_suite_words ($useTable), и возьмёт поля с таблицы, которую использует модель Word ($import). После этого она заполнит их записями из $records.
Обычно лучше вносить свои тестовые данные, но если надо брать данные из существующей таблицы то надо $import определить так
var $import = array('model' => 'Word', 'records' => true);
Тогда $records указвать не нужно. Вместо model можно указать table. Также можно указать connection.
При желании можно было бы не брать поля из существующей таблицы, а вручную указать их. Это может быть удобно, если таблица ещё не создана. Вот этот же пример с указаниеми полей.
<?
class WordTestFixture extends CakeTestFixture {
var $name = 'WordTest';
var $useTable = 'words';
var $fields = array(
'id' => array('type'=>'integer', 'key'=>'primary'),
'name' => array('type'=>'string', 'length'=>255, 'null'=>false),
'created' => 'datetime',
);
var $records = array(
array('id'=>1, 'name'=>'Table'),
array('id'=>2, 'name'=>'boy'),
array('id'=>3, 'name'=>'girl'),
);
}
?>
Опции для $fields:
Если не надо задавать дополнительных опций, то можно писать одной строкой, не создавая массив. Так например сделано в предыдущем примере для created.
Загружаем тестируемую модель
loadModel('Word');
Для того, чтобы использовать fixtures, надо создать потомка тестируемой модели и уже его тестировать.
class WordTest extends Word {
var $name = 'WordTest';
var $useTable = 'words';
var $useDbConfig = 'test_suite';
}
Дописываем тест и получаем
app/tests/cases/models/word.test.php
<?php
loadModel('Word');
class WordTest extends Word {
var $name = 'WordTest';
var $useTable = 'words';
var $useDbConfig = 'test_suite';
}
class WordTestCase extends CakeTestCase {
var $fixtures = array('word_test');
var $model = null;
function setUp() { }
function testCreate() {
$this->model =& new WordTest();
}
function testListFlipLow() {
$result = $this->model->generateListFlipLow();
$expected = array(
'table' => 1,
'boy' => 2,
'girl' => 3,
);
$this->assertEqual($result, $expected);
}
function testAdd() {
$result = $this->model->add('Test');
$this->assertEqual($result, 4);
$result = $this->model->generateListFlipLow();
$expected = array(
'table' => 1,
'boy' => 2,
'girl' => 3,
'test' => 4,
);
$this->assertEqual($result, $expected);
}
function tearDown() { }
}
?>
Обратите внимание, что setUp и tearDown запускаются до и после каждого теста.
А теперь сама модель из
app/models/word.php
<?php
class Word extends AppModel {
var $name = 'Word';
var $validate = array(
'id' => VALID_NUMBER,
'name' => VALID_NOT_EMPTY,
);
/**
* Adds a word and returns id
*
* @param string $name
* @return integer If failed to save returns false
*/
function add($name) {
$item = array(
'name' => $name,
);
$this->create();
if ($this->save($item)) {
return $this->getInsertId();
} else {
return false;
}
}
/**
* Generates list of lowercase names and its ids
*
* @return array (a(name=>id), ...)
*/
function generateListFlipLow(&$model) {
$items = $this->generateList();
foreach ($items as $id=>$name) {
$items[$id] = low($name);
}
return array_flip($items);
}
}
?>
Запускаем http://server.com/test.php и радуемся тому, что тесты работают.