четверг, 22 апреля 2010 г.

вторник, 20 апреля 2010 г.

PHPUnit. Часть 06 Дополнительные возможности

Статья из SmartyIT - Каталога статей для программиста.


PHPUnit. Часть 06 Дополнительные возможности

PHPUnit. Часть 06 Дополнительные возможности

v:1.0 01.04.2010

Перевод статьи Chapter 8. TestCase Extensions.
Автор: Sebastian Bergmann
Перевод: Петрелевич Сергей

Предисловие переводчика
Эта статья продолжает серию переводов официальной документации по PHPUnit на русский язык.
Часть 1, Часть 2, Часть 3, Часть 4, Часть 5,

PHPUnit предлагает ряд возможностей, которые расширяют функционал базовых классов.

Вывод результатов тестирования

Иногда, Вам может понадобиться проверить, что выполнение метода генерирует ожидаемый результат (например через echo или print).
Класс PHPUnit_Extensions_OutputTestCase предоставляет Вам эту возможность. Этот класс использует буферизацию вывода PHP.

Пример 8.1 демонстрирует как можно наследовать класс PHPUnit_Extensions_OutputTestCase и использовать его функцию expectOutputString(), чтобы задать ожидаемый вывод тестового метода. Если фактически полученный вывод не будет соответствовать ожиданиям, то тест будет отмечен как неудачный.

<?php
require_once 'PHPUnit/Extensions/OutputTestCase.php';

class OutputTest extends PHPUnit_Extensions_OutputTestCase
{
    public function testExpectFooActualFoo()
    {
        $this->expectOutputString('foo');
        print 'foo';
    }

    public function testExpectBarActualBaz()
    {
        $this->expectOutputString('bar');
        print 'baz';
    }
}
?>

* This source code was highlighted with Source Code Highlighter.
phpunit OutputTest  PHPUnit 3.4.2 by Sebastian Bergmann.    .F    Time: 0 seconds    There was 1 failure:    1) OutputTest::testExpectBarActualBaz  Failed asserting that two strings are equal.  --- Expected  +++ Actual  @@ -1 +1 @@  -bar  +baz    FAILURES!  Tests: 2, Assertions: 2, Failures: 1.  

В Таблице 8.1 приведены методы класса PHPUnit_Extensions_OutputTestCase.

Таблица 8.1 Методы OutputTestCase

Метод Назначение
void expectOutputRegex(string $regularExpression) Вывод должен соответствовать заданному регулярному выражению $regularExpression.
void expectOutputString(string $expectedString) Вывод должен совпадать с заданной строкой $expectedString.
bool setOutputCallback(callable $callback) Задает функцию, которая должна быть вызвана, например, для нормализации фактического результата.

PHPUnit предлагает еще два расширения PHPUnit_Extensions_Database_TestCase и PHPUnit_Extensions_SeleniumTestCase, которые описаны в Главе 9 и Главе 18, соответственно.

понедельник, 19 апреля 2010 г.

PHPUnit. Часть 05 Организация тестов

Статья из SmartyIT - Каталога статей для программиста.


PHPUnit. Часть 05 Организация тестов

PHPUnit. Часть 05 Организация тестов

v:1.0 30.03.2010

Перевод статьи Chapter 7. Organizing Tests.
Автор: Sebastian Bergmann
Перевод: Петрелевич Сергей

Предисловие переводчика
Эта статья продолжает серию переводов официальной документации по PHPUnit на русский язык.
Часть 1, Часть 2, Часть 3, Часть 4,

Комбинируемость - это одна из целей PHPUnit (см. Цели PHPUnit).
Мы хотим иметь возможность запускать несколько тестов отдельно или все вместе, например, все тесты для всего проекта целиком или все тесты для всех классов только одного компонента, который является частью проекта или тесты только для одного класса.

PHPUnit поддерживает различные способы организации тестов и объединения их в тестовый набор. В этой статье рассматривается наиболее распространенный подход.

Составление набора тестов при помощи файловой системы

Пожалуй самый простой способ составить набор тестов заключается в хранении файлов всех исходных текстов в специальном каталоге для тестов. PHPUnit может автоматически найти и запустить тесты, рекурсивно обойдя такой каталог.

Давайте посмотрим набор тестов для библиотеки Object_Freezer. Посмотрев на структуру каталогов, мы заметим, что структура тестовых классов - System Under Test (SUT), к каталоге Tests полностью повторяет структуру классов в каталоге Object.

Object                              Tests  |-- Freezer                         |-- Freezer  |   |-- HashGenerator               |   |-- HashGenerator  |   |   `-- NonRecursiveSHA1.php    |   |   `-- NonRecursiveSHA1Test.php  |   |-- HashGenerator.php           |   |  |   |-- IdGenerator                 |   |-- IdGenerator  |   |   `-- UUID.php                |   |   `-- UUIDTest.php  |   |-- IdGenerator.php             |   |  |   |-- LazyProxy.php               |   |  |   |-- Storage                     |   |-- Storage  |   |   `-- CouchDB.php             |   |   `-- CouchDB  |   |                               |   |       |-- WithLazyLoadTest.php  |   |                               |   |       `-- WithoutLazyLoadTest.php  |   |-- Storage.php                 |   |-- StorageTest.php  |   `-- Util.php                    |   `-- UtilTest.php  `-- Freezer.php                     `-- FreezerTest.php

Чтобы запустить все тесты библиотеки, нам всего лишь надо перейти в каталог с тестами PHPUnit и выполнить команду запуска:

phpunit Tests  PHPUnit 3.4.2 by Sebastian Bergmann.    ............................................................ 60 / 75  ...............    Time: 0 seconds    OK (75 tests, 164 assertions)

Чтобы выполнить все тесты, которые декларированы в классе Object_FreezerTest (Tests/FreezerTest.php), надо выполнить следующую команду:

phpunit Tests/FreezerTest  PHPUnit 3.4.2 by Sebastian Bergmann.    ............................    Time: 0 seconds    OK (28 tests, 60 assertions)

Для более точного управления тестами можно использовать переключатель --filter:

phpunit --filter testFreezingAnObjectWorks Tests  PHPUnit 3.4.2 by Sebastian Bergmann.    .    Time: 0 seconds    OK (1 test, 2 assertions)

Примечание

Недостаток этого подхода заключается в том, что мы не можем управлять последовательностью выполнения тестов. Это может привести к проблемам зависимости тестов, см. раздел "Зависимости тестов".

Составление набора тестов при помощи XML-конфигурации

Конфигурационный XML-файл PHPUnit ( Приложение C) тоже может быть применен для составления тестового набора. Пример 7.1 показывает как добавить все классы *Test, которые будут найдены в файлах *Test.php во время рекурсивного обхода каталога Tests.

Пример 7.1: Составление тестового набора при помощи XML-конфигурации

<phpunit    <testsuites>      <testsuite name="Object_Freezer">        <directory>Tests</directory>      </testsuite>    </testsuites>  </phpunit>

Примечание

У этого подхода тот же недостаток, что и в предыдущем способе - мы не можем управлять последовательностью выполнения тестов. Это может привести к проблемам зависимости тестов, см. раздел "Зависимости тестов".

В качестве альтернативы, мы можем четко прописать порядок запуска тестов:
Пример 7.2: Составление тестового набора при помощи XML-конфигурации

<phpunit    <testsuites>      <testsuite name="Object_Freezer">        <file>Tests/Freezer/HashGenerator/NonRecursiveSHA1Test.php</file>        <file>Tests/Freezer/IdGenerator/UUIDTest.php</file>        <file>Tests/Freezer/UtilTest.php</file>        <file>Tests/FreezerTest.php</file>        <file>Tests/Freezer/StorageTest.php</file>        <file>Tests/Freezer/Storage/CouchDB/WithLazyLoadTest.php</file>        <file>Tests/Freezer/Storage/CouchDB/WithoutLazyLoadTest.php</file>      </testsuite>    </testsuites>  </phpunit>

Использование класса TestSuite

В состав PHPUnit входит класс PHPUnit_Framework_TestSuite. Этот класс позволяет организовать набор тестов в иерархическом порядке.

Пример 7.3 демонстрирует класс AllTests, расположенный на вершине иерархии проекта, в состав которого входит пакет Package.

Пример 7.3: Родительский класс иерархии - AllTests

<?php
require_once 'PHPUnit/Framework.php';

require_once 'Package/AllTests.php';
// ...

class AllTests
{
    public static function suite()
    {
        $suite = new PHPUnit_Framework_TestSuite('Project');

        $suite->addTest(Package_AllTests::suite());
        // ...

        return $suite;
    }
}
?>

* This source code was highlighted with Source Code Highlighter.

Класс AllTests включает классы пакета Package_AllTests, которые в свою очередь включают тестовые классы.

Пример 7.4: Класс Package_AllTests

<?php
require_once 'PHPUnit/Framework.php';

require_once 'Framework/ClassTest.php';
// ...

class Package_AllTests
{
    public static function suite()
    {
        $suite = new PHPUnit_Framework_TestSuite('Package');

        $suite->addTestSuite('Package_ClassTest');
        // ...

        return $suite;
    }
}
?>

* This source code was highlighted with Source Code Highlighter.

Package_ClassTest - это типовой класс тестов, который наследует класс PHPUnit_Framework_TestCase.

  • Выполнение phpunit AllTests в каталоге Tests приведет к запуску всех тестов.
  • Выполнение phpunit AllTests в каталоге Tests/Package приведет к запуску тестов классов Package_*.
  • Выполнение phpunit ClassTest в каталоге Tests/Package приведет к запуску тестов класса Package_Class (которые объявлены в классе Package_ClassTest).
  • Выполнение phpunit --filter testSomething ClassTest в каталоге Tests/Package приведет к запуску теста testSomething класса Package_ClassTest.

Классы PHPUnit_Framework_TestSuite предлагают два шаблонных метода setUp() и tearDown(), которые вызываются до первого и после последнего теста тестового набора.

Пример 7.5: Класс MySuite

<?php
require_once 'MyTest.php';

class MySuite extends PHPUnit_Framework_TestSuite
{
    public static function suite()
    {
        return new MySuite('MyTest');
    }

    protected function setUp()
    {
        print __METHOD__ . "\n";
    }

    protected function tearDown()
    {
        print __METHOD__ . "\n";
    }
}
?>

* This source code was highlighted with Source Code Highlighter.

Тестовый класс MyTest, добавленный в набор тестовых методов MySuite в Примере 7.5 содержит два тестовых метода testOne() и testTwo(). В результатах работы теста видно в каком порядке вызываются функции testOne() и testTwo(), setUp() и tearDown().

MySuite::setUp()  MyTest::setUp()  MyTest::testOne()  MyTest::tearDown()  MyTest::setUp()  MyTest::testTwo()  MyTest::tearDown()  MySuite::tearDown()

Метод setUp() класса PHPUnit_Framework_TestSuite сохраняет переменные в $this->sharedFixture. Эти данные доступны во всех тестах этого набора как $this->sharedFixture (см. раздел "Совместное использование тестового окружения").

Примечание

Методы класса TestSuite setUp() и tearDown() будут вызваны даже если набор тестов не содержит тесты.

суббота, 17 апреля 2010 г.

PHPUnit. Часть 04 Тестовые окружения (Fixtures)

Статья из SmartyIT - Каталога статей для программиста.


PHPUnit. Часть 04 Тестовые окружения (Fixtures)

PHPUnit. Часть 04 Тестовые окружения (Fixtures)

v:1.0 30.03.2010

Перевод статьи Chapter 6. Fixtures.
Автор: Sebastian Bergmann
Перевод: Петрелевич Сергей

Предисловие переводчика
Эта статья продолжает серию переводов официальной документации по PHPUnit на русский язык.
Часть 1, Часть 2, Часть 3,

Установка параметров тестовой среды или, другими словами, создание тестируемого мира - это одна из самых трудоемких задач. А ведь после завершения теста всем переменным надо вернуть первоначальные значения - эта задача тоже не из простых. Тестируемый мир или параметры тестируемой среды называются - тестовое окружение (fixture).

В Примере 4.1 тестовое окружение было простым массивом, который сохранялся в переменную $stack. Однако, чаще всего тестовое окружение оказывается значительно сложнее, и количество кода, для работы с ним растет соответственно. Содержание теста может потеряться в шуме кода, отвечающего за работу с тестовым окружением. Все станет совсем плохо в тот момент, когда Вы напишите несколько тестов, использующих похожие тестовые окружения. Нам явно необходима помощь среды тестирования (framework), если мы ходим избавиться от многочисленного дублирования кода.

PHPUnit поддерживает совместное использование кода установки тестового окружения.
До того как начнет выполняться метод тестирования, будет вызван шаблонный метод setUp().
Как только метод тестирования завершит свою работу будет вызван другой шаблонный метод - tearDown(). Причем его вызов не зависит от того успешно ли завершился тест или нет.

В Примере 4.2 мы применяем подход "источник-приемник" для совместного использования тестового окружения. Но это не всегда приемлемо, а часто и вовсе невозможно. Пример 6.1 демонстрирует как можно написать тест StackTest таким образом, чтобы повторно использовать не тестовое окружение, а код, который его создает.
Прежде всего, мы объявляем переменную класса $stack, которую мы будем использовать вместо локальной переменной метода.
После этого мы переместим создание тестового окружения в метод setUp(). В заключении мы уберем ставший уже ненужным код из тестовых методов и начнем использовать новую переменную класса $this->stack, вместо локальной переменной метода $stack.

Пример 6.1: Использование setUp() для создания тестового окружения тестирования стека

<?php
class StackTest extends PHPUnit_Framework_TestCase
{
    protected $stack;

    protected function setUp()
    {
        $this->stack = array();
    }

    public function testEmpty()
    {
        $this->assertTrue(empty($this->stack));
    }

    public function testPush()
    {
        array_push($this->stack, 'foo');
        $this->assertEquals('foo', $this->stack[count($this->stack)-1]);
        $this->assertFalse(empty($this->stack));
    }

    public function testPop()
    {
        array_push($this->stack, 'foo');
        $this->assertEquals('foo', array_pop($this->stack));
        $this->assertTrue(empty($this->stack));
    }
}
?>


* This source code was highlighted with Source Code Highlighter.

Шаблонные методы setUp() и tearDown() вызываются по одному разу для каждого тестового метода (и для нового экземпляра) тестового класса.

И дополнительно, шаблонные методы setUpBeforeClass() и tearDownAfterClass() вызываются прежде, чем будет выполнен первый метод тестового класса и после того как последний метод будет завершен.

В следующем примере демонстрируются все возможные шаблонные методы, которые доступны в тестовом классе.

Пример 6.2: Пример демонстрирует применение всех возможных шаблонным методов

<?php
require_once 'PHPUnit/Framework.php';

class TemplateMethodsTest extends PHPUnit_Framework_TestCase
{
    public static function setUpBeforeClass()
    {
        print __METHOD__ . "\n";
    }

    protected function setUp()
    {
        print __METHOD__ . "\n";
    }

    protected function assertPreConditions()
    {
        print __METHOD__ . "\n";
    }

    public function testOne()
    {
        print __METHOD__ . "\n";
        $this->assertTrue(TRUE);
    }

    public function testTwo()
    {
        print __METHOD__ . "\n";
        $this->assertTrue(FALSE);
    }

    protected function assertPostConditions()
    {
        print __METHOD__ . "\n";
    }

    protected function tearDown()
    {
        print __METHOD__ . "\n";
    }

    public static function tearDownAfterClass()
    {
        print __METHOD__ . "\n";
    }

    protected function onNotSuccessfulTest(Exception $e)
    {
        print __METHOD__ . "\n";
        throw $e;
    }
}
?>


* This source code was highlighted with Source Code Highlighter.
phpunit TemplateMethodsTest  PHPUnit 3.4.2 by Sebastian Bergmann.    TemplateMethodsTest::setUpBeforeClass  TemplateMethodsTest::setUp  TemplateMethodsTest::assertPreConditions  TemplateMethodsTest::testOne  TemplateMethodsTest::assertPostConditions  TemplateMethodsTest::tearDown  .TemplateMethodsTest::setUp  TemplateMethodsTest::assertPreConditions  TemplateMethodsTest::testTwo  TemplateMethodsTest::tearDown  TemplateMethodsTest::onNotSuccessfulTest  FTemplateMethodsTest::tearDownAfterClass      Time: 0 seconds    There was 1 failure:    1) TemplateMethodsTest::testTwo  Failed asserting that <boolean:false> is true.  /home/sb/TemplateMethodsTest.php:30    FAILURES!  Tests: 2, Assertions: 2, Failures: 1.

Практика применения setUp() и tearDown()

setUp() и tearDown() теоретически должны быть полностью симметричны, однако на практике это ни так. На практике, Вы обязательно должны вызвать tearDown(), если в setUp() открыли какой-нибудь внешний ресурс, например, сокет или файл. Если setUp() создает только объекты PHP, tearDown() можно игнорировать. Однако, если в setUp() создается множество объектов, разумно в tearDown() поместить вызовы unset() для созданных объектов. Таким образом, tearDown() выполнит функцию сборщика мусора.
Сборка мусора для объектов тестовых методов практически не предсказуема.

Вариации

Что получится, если у Вас будет два теста с немного разными тестовыми окружениями? Возможны два варианта:

  • Если функции setUp() отличаются незначительно, перенесите специфический код из setUp() в тестовые методы.
  • Если функции setUp() сильно отличаются, то надо создать другой тестовый класс. Назовите этот новый класс по аналогии с отличиями в тестовом окружении.

Совместное использование тестовых окружений

Есть несколько веских причин совместного использования тестового окружения несколькими тестами, однако в большинстве случает подобная необходимость является следствием проблем в архитектуре приложения.

Хороший пример совместного использования тестового окружения несколькими тестами - соединение с базой данных. Соединение с базой устанавливается только один раз, и все тесы используют это соединение, вместо того чтобы для каждого теста создавать новое.
Эта практика позволяет ускорить выполнение тестов.

В Примере 6.3 показано как использовать шаблонные методы setUp() и tearDown() класса PHPUnit_Framework_TestSuite (см. раздел Использование класса TestSuite) для подключения к базе данных до выполнения первого теста тестового набора и отключения от базы после выполнения последнего теста. Атрибут $sharedFixture объекта PHPUnit_Framework_TestSuite доступен во всех объектах классов, унаследованных от PHPUnit_Framework_TestSuite и PHPUnit_Framework_TestCase.

Пример 6.3: Совместное использование тестового окружения несколькими тестами тестового набора

<?php
require_once 'PHPUnit/Framework.php';

class DatabaseTestSuite extends PHPUnit_Framework_TestSuite
{
    protected function setUp()
    {
        $this->sharedFixture = new PDO(
         'mysql:host=wopr;dbname=test',
         'root',
         ''
        );
    }

    protected function tearDown()
    {
        $this->sharedFixture = NULL;
    }
}
?>


* This source code was highlighted with Source Code Highlighter.

Сокращение количества тестов за счет применения совместного использования тестового окружения должно настораживать. Это может говорить о наличии скрытой пробемы в архитектуре - объекты излишне взаимосвязаны.
Вы получите значительно лучший результат, если сначала решите эту скрытую проблему, а только потом напишите тесты, используя заглушки (stubs) (см. Глава 11). Это намного лучше, чем создание зависимостей между тестами и игнорирование возможности улучшить архитектуру.

Глобальное состояние

Очень трудно тестировать код, который использует паттерн singleton (В приложении создается только один экземпляр класса. Таким образом реализуется функционал глобальных переменных. Прим. переводчика). Это утверждение справедливо и для случаев применения глобальных переменных. Часто, код, который Вы хотите протестировать сильно связан с глобальными переменными, создание которых Вы не можете контролировать. Другая проблема заключается в том, что один тест может изменить значение глобальной переменной, из-за чего сломается другой тест.

В PHP, гобальные переменные работают так:

  • Глобальная переменная $foo = 'bar'; сохраняется как $GLOBALS['foo'] = 'bar';.
  • Переменная $GLOBALS по другому называется суперглобальная.
  • Суперглобальные переменные - это встроенные переменные, которые доступны во всех модулях приложения.
  • В пределах функции или метода Вы можете получить доступ к глобальной переменной $foo или используя прямой доступ $GLOBALS['foo'], или применив ключевое слово global $foo; для создания локальной переменной, которая будет связана с глобальной.

Кроме глобальных переменных к глобальным состояниям относятся еще и атрибуты классов.

По умолчанию, PHPUnit выполняет Ваши тесты таким образом, что изменение глобальных и суперглобальных переменных ($GLOBALS, $_ENV, $_POST, $_GET, $_COOKIE, $_SERVER, $_FILES, $_REQUEST) не влияет на другие тесты. Дополнительно, эта изоляция может быть расширена и на статические атрибуты классов.

Примечание

Применение операций резервного хранения и восстановления статических атрибутов классов требует PHP 5.3 (или выше).

Операции резервного хранения и восстановления глобальных переменных и статических атрибутов классов используют функции serialize() и unserialize().

Объекты некоторых классов, которые входят в состав PHP, например PDO, не могут быть сохранены и восстановлены. Попытка сохранить подобный объект в массив $GLOBALS завершится с ошибкой.

Операциями сохранения и восстановления глобальных переменных можно управлять при помощи аннотации @backupGlobals, см. раздел @backupGlobals. В качестве альтернативы, Вы можете составить список переменных, с которыми операции сохранения и восстановления не должны работать, см. код ниже:

class MyTest extends PHPUnit_Framework_TestCase  {      protected $backupGlobalsBlacklist = array('globalVariable');        // ...  }

Примечание

Пожалуйста, учтите, что установка атрибута $backupGlobalsBlacklist внутри метода, например, setUp() не будет иметь эффект.

Операциями сохранения и восстановления статических атрибутов можно управлять при помощи аннотации @backupStaticAttributes, см. раздел @backupStaticAttributes В качестве альтернативы, Вы можете составить список статических атрибутов, с которыми операции сохранения и восстановления не должны работать, см. код ниже:

class MyTest extends PHPUnit_Framework_TestCase  {      protected $backupStaticAttributesBlacklist = array(        'className' => array('attributeName')      );        // ...  }

Примечание

Пожалуйста, учтите, что установка атрибута $backupStaticAttributesBlacklist внутри метода, например, setUp() не будет иметь эффект.

пятница, 16 апреля 2010 г.

PHPUnit. Часть 08 Практика тестирования

В Каталог статей для программиста добавлена новая статья.


PHPUnit. Часть 08 Практика тестирования

Перевод двенадцатой главы официальной документации по PHPUnit, в статье рассказывается о некоторых приемах применения модульных тестов.

PHPUnit. Часть 03 Написание тестов для PHPUnit

Статья из SmartyIT - Каталога статей для программиста.


PHPUnit. Часть 03 Написание тестов для PHPUnit

PHPUnit. Часть 03 Написание тестов для PHPUnit

v:1.0 25.03.2010

Перевод статьи Chapter 4. Writing Tests for PHPUnit.
Автор: Sebastian Bergmann
Перевод: Петрелевич Сергей

Предисловие переводчика
Эта статья продолжает серию переводов официальной документации по PHPUnit на русский язык.
Часть 1, Часть 2

Пример 4.1 демонстрирует как с помощью PHPUnit можно выполнить тестирование операций с массивами PHP. В этом примере показаны базовые соглашения и шаги, свойственные тестам PHPUnit:

  • Наименование тестирующего класса образуется путем добавления постфикса Test к наименованию тестируемого класса. Например тестируемый класс называется Class, тестирующий - ClassTest.
  • ClassTest наследуется (в большинстве случаев) от PHPUnit_Framework_TestCase.
  • Наименования тестирующих методов образуются путем добавления приставки test к наименованиям тестируемых методов.
  • Внутри тестовых методов утверждения задаются специальными функциями assertEquals() (смотрите раздел PHPUnit_Framework_Assert). assertEquals() задает соответствие реально полученного значения и ожидаемого.

Пример 4.1: Тестирование операций с массивами PHP при с использованием PHPUnit

<?php
require_once 'PHPUnit/Framework.php';

class StackTest extends PHPUnit_Framework_TestCase
{
    public function testPushAndPop()
    {
        $stack = array();
        $this->assertEquals(0, count($stack));

        array_push($stack, 'foo');
        $this->assertEquals('foo', $stack[count($stack)-1]);
        $this->assertEquals(1, count($stack));

        $this->assertEquals('foo', array_pop($stack));
        $this->assertEquals(0, count($stack));
    }
}
?>


* This source code was highlighted with Source Code Highlighter.

"Пишите тест в независимости от того хотите ли Вы вывести значение через функцию print или выражение отладки." Martin Fowler

Зависимости тестов

"Unit-тесты обычно пишутся, чтобы помочь разработчику найти и исправить ошибки, выполнить рефакторинг кода и облегчить документирование модулей. Чтобы достичь эти цели, тесты в идеале должны покрывать все возможные пути в программе. Обычно один тест покрывает один специфический путь в одной функции или методе. Однако тестовый метод не обязательно может быть инкапсулированной, независимой сущностью. Часто существуют не очевидные зависимости между тестами, скрытые в реализации тестовых сценариев". Adrian Kuhn et. al.

PHPUnit поддерживает декларирование явных зависимостей между тестами. Такие зависимости не определяют в какой последовательности тесты должны выполняться, однако они позволяют возвращать экземпляр тестового окружения (fixture) и передавать его в другой тест; источник (producer) передает приемнику(consumer).

  • Источник - это тестовый метод, который возвращает значения, от которых зависят другие методы модуля.
  • Приемник - это тестовый метод, который зависит от одного или более источников и их возвращаемых значений.

Пример 4.2 показывает как использовать аннотацию @depends для описания зависимостей между тестами.

Пример 4.2: Использование аннотации @depends для описания зависимостей

<?php
class StackTest extends PHPUnit_Framework_TestCase
{
    public function testEmpty()
    {
        $stack = array();
        $this->assertTrue(empty($stack));

        return $stack;
    }

    /**
     * @depends testEmpty
     */
    public function testPush(array $stack)
    {
        array_push($stack, 'foo');
        $this->assertEquals('foo', $stack[count($stack)-1]);
        $this->assertFalse(empty($stack));

        return $stack;
    }

    /**
     * @depends testPush
     */
    public function testPop(array $stack)
    {
        $this->assertEquals('foo', array_pop($stack));
        $this->assertTrue(empty($stack));
    }
}
?>


* This source code was highlighted with Source Code Highlighter.

В этом примере первый тест, testEmpty(), создает пустой массив и задает утверждение, что массив пустой. После этого тест возвращает окружение (fixture) в качестве результата. Второй тест, testPush(), зависит от testEmpty() и получает результат работы testEmpty() в качестве аргумента.
И наконец, testPop() зависит от testPush().

Мы хотим, чтобы наше внимание фокусировалось на важных сообщениях о провале тестов, это позволит максимально быстро находить дефекты. По этой причине PHPUnit пропускает выполнение зависимых тестов, если Источник (основной тест) закончил работу с ошибкой. Улучшение обнаружения дефектов достигается за счет использования зависимостей между тестами, как показано в Примере 4.3.

Пример 4.3: Использование зависимостей между тестами

<?php
class DependencyFailureTest extends PHPUnit_Framework_TestCase
{
    public function testOne()
    {
        $this->assertTrue(FALSE);
    }

    /**
     * @depends testOne
     */
    public function testTwo()
    {
    }
}
?>


* This source code was highlighted with Source Code Highlighter.
  phpunit --verbose DependencyFailureTest  PHPUnit 3.4.2 by Sebastian Bergmann.    DependencyFailureTest  FS    Time: 0 seconds    There was 1 failure:    1) testOne(DependencyFailureTest)  Failed asserting that  is true.  /home/sb/DependencyFailureTest.php:6    There was 1 skipped test:    1) testTwo(DependencyFailureTest)  This test depends on "DependencyFailureTest::testOne" to pass.    FAILURES!  Tests: 2, Assertions: 1, Failures: 1, Skipped: 1.  

У теста может быть несколько аннотаций @depends.
PHPUnit не изменяет последовательность, в которой запускаются тесты, Вы должны быть уверены, что зависимости будут выполнены до того как тест запустится.

Источники данных (Data Providers)

Тестовый метод может работать с произвольными аргументами. Аргументы передаются с помощью метода источника данных (provider(), см. Пример 4.4). Метод источника данных должен быть определен с помощью аннотации @dataProvider.

Метод источника данных должен быть public и должен возвращать массив массивов или объект, поддерживающий интерфейс Iterator, который на каждой итерации возвращает массив. Для каждого массива, который является частью коллекции, будет вызван тестовый метод. В качестве аргумента методу будет передан массив значений.

Пример 4.4: Использование источника данных

<?php
class DataTest extends PHPUnit_Framework_TestCase
{
    /**
     * @dataProvider provider
     */
    public function testAdd($a, $b, $c)
    {
        $this->assertEquals($c, $a + $b);
    }

    public function provider()
    {
        return array(
         array(0, 0, 0),
         array(0, 1, 1),
         array(1, 0, 1),
         array(1, 1, 3)
        );
    }
}
?>


* This source code was highlighted with Source Code Highlighter.
  phpunit DataTest  PHPUnit 3.4.2 by Sebastian Bergmann.    ...F    Time: 0 seconds    There was 1 failure:    1) testAdd(DataTest) with data (1, 1, 3)  Failed asserting that  matches expected value .  /home/sb/DataTest.php:21    FAILURES!  Tests: 4, Assertions: 4, Failures: 1.  

Примечание

Если тестовому методу одновременно передаются параметры от источника данных (@dataProvider) и одного или нескольких тестов, которые определены как зависимые (@depends), то в первую очередь используется источник данных и только потом другие тесты.

Тестирование исключений

Пример 4.5 демонстрирует как использовать аннотацию @expectedException для тестирования исключений, которые выбрасываются внутри тестового кода.

Пример 4.5: Использование аннотации @expectedException

<?php
require_once 'PHPUnit/Framework.php';

class ExceptionTest extends PHPUnit_Framework_TestCase
{
    /**
     * @expectedException InvalidArgumentException
     */
    public function testException()
    {
    }
}
?>


* This source code was highlighted with Source Code Highlighter.
  phpunit ExceptionTest  PHPUnit 3.4.2 by Sebastian Bergmann.    F    Time: 0 seconds    There was 1 failure:    1) testException(ExceptionTest)  Expected exception InvalidArgumentException    FAILURES!  Tests: 1, Assertions: 1, Failures: 1.  

Метод setExpectedException() - это еще один способ указать, что в тестовом методе выбрасывается исключение, см. Пример 4.6.

Пример 4.6: Ожидается, что тестовый метод выбросит исключение

<?php
require_once 'PHPUnit/Framework.php';

class ExceptionTest extends PHPUnit_Framework_TestCase
{
    public function testException()
    {
        $this->setExpectedException('InvalidArgumentException');
    }
}
?>


* This source code was highlighted with Source Code Highlighter.
  phpunit ExceptionTest  PHPUnit 3.4.2 by Sebastian Bergmann.    F    Time: 0 seconds    There was 1 failure:    1) testException(ExceptionTest)  Expected exception InvalidArgumentException    FAILURES!  Tests: 1, Assertions: 1, Failures: 1.  

В таблице 4.1 приведены методы для тестирования исключений.
Таблица 4.1. Методы для тестирования исключений

Метод Назначение
void setExpectedException(string $exceptionName) Установка имени ожидаемого исключения - $exceptionName.
String getExpectedException() Получение имени ожидаемого исключения.

Для тестирования исключений Вы можете использовать подход, показанный в Примере 4.7.

Пример 4.7: Альтернативный подход к тестированию исключений

<?php
require_once 'PHPUnit/Framework.php';

class ExceptionTest extends PHPUnit_Framework_TestCase {
    public function testException() {
        try {
            // ... Code that is expected to raise an exception ...
        }

        catch (InvalidArgumentException $expected) {
            return;
        }

        $this->fail('An expected exception has not been raised.');
    }
}
?>


* This source code was highlighted with Source Code Highlighter.

Ожидается, что код, показанный в Примере 4.7 выбросит исключение. Если это не произойдет, то будет вызван метод fail(), (см. (см. Таблицу 22.2), который прервет выполнение теста и просигнализирует об ошибке. Если исключение будет сгенерированно, то сработает блок catch и тест завершится успешно.

Тестирование ошибок PHP

По умолчанию, ошибки, предупреждения и уведомления PHP, которые появляются во время тестирования, PHPUnit преобразует в исключения. Используя эту особенность, Вы можете, например, настроить механизм ожидания появления подобного исключения в тесте, см. Пример 4.8.

Пример 4.8: Применение @expectedException для ожидяния ошибки PHP

<?php
class ExpectedErrorTest extends PHPUnit_Framework_TestCase
{
    /**
     * @expectedException PHPUnit_Framework_Error
     */
    public function testFailingInclude()
    {
        include 'not_existing_file.php';
    }
}
?>


* This source code was highlighted with Source Code Highlighter.
  phpunit ExpectedErrorTest  PHPUnit 3.4.2 by Sebastian Bergmann.    .    Time: 0 seconds    OK (1 test, 1 assertion)  

PHPUnit_Framework_Error_Notice и PHPUnit_Framework_Error_Warning представляют PHP уведомления и предупреждения.

четверг, 15 апреля 2010 г.

PHPUnit. Часть 02 Цели PHPUnit

Статья из SmartyIT - Каталога статей для программиста.


PHPUnit. Часть 02 Цели PHPUnit

PHPUnit. Часть 02 Цели PHPUnit

v:1.0 21.03.2010

Перевод статьи Chapter 2. PHPUnit's Goals.
Автор: Sebastian Bergmann
Перевод: Петрелевич Сергей

Предисловие переводчика
Эта статья продолжает цикл про PHPUnit, первая часть находится здесь.

Итак, у нас есть только два теста для встроенного массива и функции sizeof(). Когда мы начнем тестировать все бесчисленные встроенные функции, которые предлагает PHP для работы с массивом, нам придется написать по одному тесту для каждой из них. Конечно, мы могли бы написать необходимую инфраструктуру с нуля. Однако намного лучше использовать ранее написанную инфраструктуру, и доделывать только отдельные специфические части.
PHPUnit - это пример такой инфраструктуры.

Инфраструктура (framework) PHPUnit должна решить ряд ограничений, причем некоторые из этих ограничений пересекаются.
Тесты можно считать хорошими, если их:

Легко научиться писать.
Если обучиться написанию тестов трудно, разработчики не будут это делать.
Легко написать.
Если написать тесты трудно, разработчики не будут это делать.
Легко прочитать.
Тест не должен содержать какие-либо внешние переопределения, чтобы тест не потерялся в шуме, который его окружает.
Легко выполнить.
Тест должен выполняться легким нажатием кнопки и выводить результат в понятном и недвусмысленном формате.
Быстро выполнить.
Тесты должны выполняться очень быстро, чтобы была возможность выполнять тестирование сотни тысяч раз в день.
Изолированность.
Тесты не должны влиять друг на друга. Если порядок выполнения тестов изменяется, итоговый результат меняться не должен.
Комбинируемость.
У нас должна быть возможность запускать любую комбинацию тестов. Это следствие изолированности.

У этих ограничений есть два противоречия:

Легкость обучения написанию тестов противоречит легкости написания.
В большинстве своем, тесты не требуют всей гибкости языков программирования. Многие средства тестирования предоставляют свой скриптовый язык, который имеет только минимально необходимые функции для написания тестов. Используя такие скрипты, легко написать нужные тесты, которые потом можно легко прочитать, такие тесты не содержат синтаксические конструкции, которые отвлекают от содержания.
Однако, изучение скриптового языка и набора программных средств может быть достаточно сложным делом.
Изолированность противоречит скорости выполнения.
Если Вы хотите, чтобы результаты одного теста не влияли на результаты других, то для выполнения каждого теста Вам надо полностью создать состояние среды тестирования и восстановить первоначальное состояние после того как тест будет завершен.
Однако, создание состояния среды тестирования может занять длительное время. Например, подключение к базе данных и инициализация достоверными данными.

PHPUnit старается разрешить эти противоречия, используя PHP как язык написания тестов. Иногда вся мощь PHP избыточна для написания простых тестов и заметно их усложняет, однако применяя PHP, мы используем ранее накопленный опыт и множество уже готовых и отлаженных инструментальных средств. Если мы пытаемся убедить ленивых тестировщиков использовать новое средство, то максимальная простота написания первых тестов становится очень важным условием.

PHPUnit больше склоняется в сторону изолированности в ущерб скорости выполнения. Изолированные тесты очень важны, поскольку они обеспечивают высококачественную обратную связь. Если первый тест серии тестов закончится неудачно, то такая неудача не повлияет на выполнение других тестов. В результате Вы получите отчет о выполнении отдельных независимых тестов, а не запутанный клубок непонятных результатов, из которого не ясно то ли тест не сработал сам, то ли другие тесты на него негативно повлияли.
Использование изолированных тестов поощряет разработчика использовать большое количество простых объектов. Изолированно можно быстро проверить все эти объекты. В результате архитектура становится лучше, а тесты быстрее.

PHPUnit предполагает, что большинство тестов завершается успешно, и нет необходимости выводить детальные сведенья о них. Если тест заканчивается неудачно, этот факт обязательно фиксируется в отчете. Большинство тестов должно завершиться успешно, успешные результаты никак не комментируются, отмечается только общее число выполненных тестов. Это предположение реализовано на уровне классов отчетов, а не ядра PHPUnit. В отчетах о результатах тестирования отмечается общее число выполненных тестов и детальная информация о тестах, которые закончились с ошибкой.

Тест должен быть минимально возможного размера, и должен проверять только одну характеристику объекта. Поэтому, если тест в первый раз заканчивается неудачей, то выполнение тестов прерывается, и PHPUnit сообщает об ошибке. Это своего рода искусство, выполнять тестирование при помощи множества маленьких тестов. Применение микротестов позволяет в целом улучшить архитектуру системы.

Тестировать объект при помощи PHPUnit возможно только через публичный интерфейс. Тестирование, основанное только на публично видимом поведении, позволяет выявить и устранить проблемы архитектуры до того, как эти проблемы распространятся на другие части системы.

среда, 14 апреля 2010 г.

PHPUnit. Часть 01 Автоматические тесты

Статья из SmartyIT - Каталога статей для программиста.


PHPUnit. Часть 01 Автоматические тесты

PHPUnit. Часть 01 Автоматические тесты

v:1.0 17.03.2010

Перевод статьи PHPUnit Manual. Chapter 1. Automating Tests.
Автор: Sebastian Bergmann
Перевод: Петрелевич Сергей

Предисловие переводчика
Недавно начал изучать PHPUnit (framework семейства xUnit) и с удивлением обнаружил, что на русском языке нет статей про автоматические тесты для самых-самых чайников.
В первой главе документации по PHPUnit на примерах очень доступно рассказывается, что такое автоматическое тестирование.

Даже хорошие программисты допускают ошибки. Разница между хорошим программистом и плохим заключается в том, что хороший программист как можно чаще использует тесты, чтобы найти свои ошибки. Чем раньше Вы начнете тестировать, тем выше Ваши шансы найти ошибку, и тем ниже цена исправления. Это объясняет, почему откладывание тестирования до момента передачи программы заказчику является очень плохой практикой. Большинство ошибок так и не будет найдено, а цена исправления станет такой высокой, что Вам придется составить большой график работы, т.к. сразу Вы не сможете их все исправить.

Тестирование при помощь PHPUnit принципиально не отличается от того тестирования, которое Вы уже выполняете. Это просто другой подход к работе. Разница заключается в следующем: в одном случае Вы просто проверяете, что Ваша программа работает как ожидается, в другом - Вы выполняете серию тестов, которые представляют собой выполняемые фрагменты кода для автоматической проверки корректности частей (модулей) программного обеспечения.
Эти выполняемые фрагменты кода называются модульными (Unit) тестами.

В этой статье мы проделаем путь от элементарного теста, который просто выводит на экран результат своей работы до полностью автоматизированного теста.
Допустим, нас попросили протестировать встроенный в PHP массив (array). На одном из этапов тестирования надо проверить работу функции count(). Мы ожидаем, что для только что созданного массива функция count() вернет 0. После добавления элемента в массив count() должна вернуть 1.
Пример 1.1 демонстрирует, что мы хотим проверить.

Пример 1.1: Тестирование операторов массива

<?php
$fixture = array();
// $fixture is expected to be empty.

$fixture[] = 'element';
// $fixture is expected to contain one element.
?>


* This source code was highlighted with Source Code Highlighter.

Самый простой способ проверить, получили ли мы то, что хотели - вывести результат работы функции count() на экран.
Вывод надо сделать до и после добавления элемента, смотрите Пример 1.2.
Если мы сначала получили 0 а потом 1, то array и count() работают как надо.

Пример 1.2: Использование вывода на экран для проверки операторов массива

<?php
$fixture = array();
print count($fixture) . "\n";

$fixture[] = 'element';
print count($fixture) . "\n";
?>


* This source code was highlighted with Source Code Highlighter.
Вывод теста:
0   1

Мы бы хотели перейти от тестов, которые требуют ручной обработки результатов к тестам, которые могут выполняться автоматически.
В Примере 1.3 мы добавим в тест сравнение ожидаемого результата и фактического значения, выведем ok если ожидаемый и фактический результаты совпали.
Если вывод будет not ok, значит где-то произошла ошибка.

Пример 1.3: Тестирование операторов массива, сравнение ожидаемого результата и фактического значения

<?php
$fixture = array();
print count($fixture) == 0 ? "ok\n" : "not ok\n";

$fixture[] = 'element';
print count($fixture) == 1 ? "ok\n" : "not ok\n";
?>


* This source code was highlighted with Source Code Highlighter.
Вывод теста:
ok  ok

А сейчас вынесем сравнение ожидаемого и фактического результата в специальную функцию, которая выбрасывает исключение, если условие не выполняется, смотрите Пример 1.4.
Этот подход дает нам два преимущества: написание тестов заметно упрощается, тест генерирует сообщение только в случае ошибки.

Пример 1.4: Использование функции утверждения для тестирования операторов массива

<?php
$fixture = array();
assertTrue(count($fixture) == 0);

$fixture[] = 'element';
assertTrue(count($fixture) == 1);

function assertTrue($condition)
{
  if (!$condition) {
     throw new Exception('Assertion failed.');
  }
}
?>


* This source code was highlighted with Source Code Highlighter.

Тест полностью автоматизирован. Вместо просто тестирования, которое мы выполняли в первой версии теста, мы получили автоматический тест.

Цель использования автоматических тестов - допускать меньше ошибок в коде.
Даже если Ваш код все еще не идеален, и Вы используете отличные тесты, начните практиковать автоматические тесты и Вы обнаружите значительное сокращение количества ошибок.
Автоматические тесты обеспечат Вам уверенность в своем коде. Опираясь на эту уверенность, Вы сможете вносить более смелые и решительные изменения в свою программу (Рефакторинг), сможете улучшить взаимоотношения с коллегами и клиентами.
Каждый день, уходя домой, Вы будете знать, что программа стала значительно лучше, чем она была утром.

Формы в Zend Framework

Статья из SmartyIT - Каталога статей для программиста.


Формы в Zend Framework

Формы в Zend Framework

v:1.0 19.02.2010

Перевод статьи Forms in Zend Framework.
Автор: Adrian Schneider

Меня часто спрашивают, какой мой любимый компонент в Zend Framework, и я всегда отвечаю: "Forms" (формы).
В парадигме модель-представление-контроллер формы всегда играют непростую роль. Конечно, форма - это всего лишь HTML, но для меня это нечто более абстрактное.
По сути, форма - это HTML, средствами которого пользователь вводит и получает данные, но кроме этого форма еще выполняет нормализацию, проверку, фильтрацию данных и вывод сообщений об ошибках, если они есть.
Это может потребовать довольно-таки значительного объема кода.

Если Вы можете автоматизировать все эти вещи, то Вам останется только конфигурация.
Каждая форма состоит из элементов, каждый элемент имеет несколько атрибутов, которые переопределяют функциональность формы.
Давайте рассмотрим некоторые из этих аспектов.

Создание формы

Форма - это экземпляр класса Zend_Form. Экземпляр можно создать многими способами.
Я предпочитаю создавать свои классы, наследуя класс Zend_Form. И уже в свои классы вношу необходимые мне изменения. Каждая форма состоит из нескольких важных полей (действие, метод) и содержит как минимум один элемент. Давайте посмотрим, как можно создать элемент.
Каждый элемент может иметь несколько типовых атрибутов: тип поля, метка, наименование и описание.
Давайте опишем такой элемент в ZF.
Обратите внимание, существует несколько различных способов создания форм. Это мой самый любимый способ.

$username = new Zend_Form_Element_Text('username', array(
        'label'       => 'Username',
        'description' => 'This is a description'));


* This source code was highlighted with Source Code Highlighter.

Как Вы видите, в примере я задаю все эти атрибуты.

Прим. переводчика:
тип поля - Zend_Form_Element_Text
метка (label) - 'Username'
наименование - 'username'
описание (description) - 'This is a description'


Кроме указанных выше есть еще несколько важных атрибутов элемента. Давайте добавим еще несколько.

$this->addElements(array(
    new Zend_Form_Element_Text('username', array(
     'label' => 'Username',
     'required' => true)),
    ));


* This source code was highlighted with Source Code Highlighter.

Фильтры и валидаторы

Выстраивание цепочек из фильтров и валидаторов - это одна из очень полезных особенностей компонента форм.
Фильтр - это такая штука, которая автоматически выполняет преобразования входных данных. А валидатор - это механизм автоматической проверки данных во время обработки форм. Эти механизмы существенно упрощают код контроллера и модели, т.к. логика перемещается в более организованную структуру.

Рассмотрим пример. На регистрационной форме находится элемент ввода имени пользователя. Надо реализовать проверку уникальности имени, имя должно состоять только из разрешенных символов и его длина должна входить в допустимый диапазон.

Если у Вас на форме есть элемент, в котором пользователь может ввести произвольный текст, например о себе, то Вы наверняка захотите применить фильтры, чтобы убедиться, что введены "чистые" данные.
Будет полезно добавить фильтры типа StripTags, чтобы убрать все теги HTML, StringTrim, чтобы убрать все лишние пробелы. Если есть желание, можно добавить фильтры для обработки BB-кодов. Для этого вы можете создать свой собственный фильтр или использовать Callback-фильтр, который сможете использовать и в других формах.

Идея заключается в том, чтобы проверять данные как можно более тщательно, Вы должны быть уверены, что данные на 100% соответствуют вашему приложению. Если у Вас поле типа VARCHAR(255), то ограничьте поле ввода 255 символами, и Вам не придется пользоваться функциями обрезания строк. Проявляйте максимум осторожности.

Фильтры помогают сохранить целостность данных, также могут использоваться в процессе очистки данных, введенных в форме. Если Вы позволяете пользователю что-то вводить, обязательно удаляйте все теги HTML. Думаю, Вы поняли идею...

Представляю более полный пример регистрационной формы:

$this->addElements(array(
  new Zend_Form_Element_Text('username', array(
    'label'    => 'Username',
    'required'   => true,
    'validators'  => array(
      array('StringLength',   false, array(4, 16)),
      array('Alnum'),
      array('Db_NoRecordExists', false, array('users', 'username'))
    )
  )),
  new Zend_Form_Element_Text('email', array(
    'label'    => 'Email Address',
    'required'   => true,
    'validators'  => array(
      array('EmailAddress'),
      array('Db_NoRecordExists', false, array('users', 'email'))
    )
  )),
  new Zend_Form_Element_Password('password', array(
    'label'    => 'Password',
    'required'   => true
  ))
));


* This source code was highlighted with Source Code Highlighter.

Худой контроллер, толстая модель

Для формы, описанной выше, Вы можете разработать очень простой контроллер. Я считаю, что контроллер должен управлять логикой приложения, но не должен работать с данными. Вот пример действия (action) для формы регистрации.

if ($this->_request->isPost())
{
  if ($form->isValid($this->_request->getPost()))
  {
    $user->register($form->getValues());
    $this->redirector->goToUrl('/welcome');
  }
}
$this->view->form = $form;


* This source code was highlighted with Source Code Highlighter.

После того как пользователь заканчивает ввод данных, валидаторы выполняют проверку введенной информации. Если проверка заканчивается успешно, массив данных передается в модель. К моменту передачи данных в модель все фильтры и валидаторы должны закончить свою работу. Сейчас мы уже знаем как форматировать данные и как повысить их безопасность. В контроллере Вы можете перенаправить пользователя на другую страницу или выполнить другие действия в соответствии с логикой Вашего приложения.

Форма выводится пользователю через представление. В общем случае представление просто загружает форму и отображает ее пользователю. Если введенные пользователем данные содержат ошибку, представление не только отобразит форму и выведет сообщения об ошибках, но и покажет введенные данные.

Скрипты представления и декораторы

Ниже показан весь код представления, с помощью которого можно отобразить форму. Если требуется дополнительная настройка формы, то ее можно выполнить средствами CSS или декораторами.

<?= $this->form ?>

* This source code was highlighted with Source Code Highlighter.

По умолчанию, Zend_Form формирует вполне приличный код HTML. Я не испытываю какие-либо сложности с наложением дополнительных стилей CSS. Использование CSS хорошо вписывается в концепцию разделения различных функциональных частей приложения.
Однако, часто требуется изменить не только стиль отображения, но и расположение элементов. В Zend для этого используются декораторы. Декораторы несколько сложны для понимания, поэтому я оставлю их для следующего поста.

Взаимодействие с моделями

В соответствии с кодом нашего контроллера, в модель передается результат функции $form->getValues(). Результат этой функции - предварительно проверенный и отфильтрованный массив данных.
Вот пример:

// ...
public function register(array $user) {

}
// ...


* This source code was highlighted with Source Code Highlighter.

Я считаю, что это прекрасно. Мы работаем с очень простым интерфейсом, и нам не надо вручную указывать каждое значение, которое мы хотим передать. Конечно, это дело хозяйское, но я не понимаю людей, которые передают в модель по 5-10 значений. Такие контроллеры непременно будут большими.
Разумеется, код не зависит от формы, поскольку работает с массивом данных. Это делает вызов модели очень простым. Модель - это место, где должен начинаться сложный код.
При таком подходе мы можем использовать наш объект адаптера базы данных или несколько других библиотек.

Заключение

Комонент форма (form) - это один из самых больших, а может быть и самый большой компонент в Zend. Не бойтесь задавать вопросы на различных форумах, или задавайте вопросы в форме ниже, я направлю Вас на хороший ресурс. Как я уже говорил, для меня это самый полезный компонент в Zend. Я настоятельно рекомендую не пожалеть время на его изучение.

Возможно, Вас заинтересует материал по кэшированию форм.

Петрелевич Сергей
petrelevich@yandex.ru
www.SmartyIT.ru

вторник, 13 апреля 2010 г.

Обновление dayjam

Сегодня закончил SEO-оптимизацию сайта Поздравления с днем роджения.
Надеюсь, новая оптимизированная структура улучшит позиции в выдаче поисковиков.

четверг, 8 апреля 2010 г.

Бесшумный компьютер

В Каталог статей для программиста добавлена новая статья.


Бесшумный компьютер

Статья о том как сделать компьютер тише.

>PHPUnit. Первый PHPUnit-тест для Zend Framework

В Каталог статей для программиста добавлена новая статья.


>PHPUnit. Первый PHPUnit-тест для Zend Framework

В статье рассмотрен простейший PHPUnit-тест приложения Zend Framework.

среда, 7 апреля 2010 г.

PHPUnit. Установка PHPUnit в MS Windows.

В Каталог статей для программиста добавлена новая статья.


PHPUnit. Установка PHPUnit в MS Windows.

В статье описывается процесс установки PHPUnit в Windows.

суббота, 3 апреля 2010 г.

PHPUnit. Часть 07 Тестирование базы данных

В Каталог статей для программиста добавлена новая статья.


PHPUnit. Часть 07 Тестирование базы данных

Перевод девятой главы официальной документации по PHPUnit, в статье рассказывается о тестировании базы данных средствами PHPUnit.

четверг, 1 апреля 2010 г.

PHPUnit. Часть 06 Дополнительные возможности

В Каталог статей для программиста добавлена новая статья.


PHPUnit. Часть 06 Дополнительные возможности

Перевод восьмой главы официальной документации по PHPUnit, в статье рассказывается о дополнительных возможностях, которые предлагает PHPUnit.