Статьи

Zend_Test для принятия TDD

Acceptance Test-Driven Development — гибкая технология, которая расширяет подход, основанный на тестировании, до разработки внешнего интерфейса приложения. Механизм Acceptance TDD понятен: сначала вы пишете тест, который определяет цель вашего развития, а именно функцию, которую вы добавляете в свое приложение. Как и во всех вариантах TDD, этот тест не пройден.

После добавления неуспешного теста вы добавляете только необходимый код, чтобы он прошел, и как только индикатор станет зеленым, вы сможете легко выполнить рефакторинг. Обратите внимание, что в этом случае для перехода к зеленой полосе может потребоваться много меньших циклов TDD, когда вы пишете модульные тесты и выполняете их; эти тесты намного более детализированы, и Acceptance TDD помогает вам решить, какие модульные тесты написать (подсказка: те, которые будут служить вашему внешнему интерфейсу, чтобы сделать исходный приемочный тест зеленым).

Принятие TDD обладает всеми преимуществами TDD, применяемыми на широком уровне. Вероятно, наибольшим преимуществом является предоставление определения того, что считается выполненным: функция завершается, когда все ее приемочные испытания проходят. Другим преимуществом использования Acceptance TDD является простое создание набора регрессионных тестов на сквозном уровне, который обнаруживает большинство ошибок проводки (объект, который не был создан или создан неправильно, различные контракты на стороне поставщика и клиента) ,

Код, представляющий собой приемочные тесты, может быть подробным и должен быть особенно хорошо поддержан и подвергнут рефакторингу, чтобы избежать хрупкости набора тестов. Декларативный уровень должен быть построен на базовой тестовой среде, чтобы исключить дублирование тестового кода и иметь возможность изменять специфику внешнего интерфейса (в классах и идентификаторах CSS веб-приложений или общей структуре DOM) только в двух места: один тестовый класс и производственный код.

Zend_Test

Zend_Test является компонентом Zend Framework, который имитирует цикл запроса / ответа для приложений, созданных с помощью стека этой инфраструктуры MVC (прежде всего Zend_Controller и Zend_View). Все тесты выполняются в одном процессе памяти (без прямого обращения к HTTP-серверам и т. П.), Но на самом деле они представляют собой сквозные тесты, поскольку они выполняют приложение из точки входа URI. В настоящее время единственная реализация контрольных примеров Zend_Test предназначена для PHPUnit, и она принимает форму пользовательского контрольного примера с дополнительными методами для отправки поддельных HTTP-запросов и выполнения утверждений, связанных с ответом.

Некоторые компоненты отключаются автоматически из-за природы нативного PHP Api (один для всех: Zend_Session, который становится массивом вместо фактической установки файлов cookie и запуска сеансов), в то время как другие должны быть отключены по желанию, чтобы перехватить вычисления, которые идут слишком вдали от самого приложения (адаптеры базы данных, замененные облегченными версиями, которые могут быть выброшены в конце тестового прогона, или Zend_Mail, замененные почтовой программой-заглушкой, которая перехватывает исходящие сообщения, чтобы на них могли выполняться утверждения).

Возможностей Zend_Test множество, поскольку он использует преимущества стековой архитектуры MVC и обертывания основных функций PHP. Например, перенаправления или заголовки ошибок отслеживаются, а не отправляются с header () (что может вызвать ошибки в среде командной строки).

Поддельные запросы, очевидно, могут устанавливать пользовательские параметры GET или POST или любые виды заголовков HTTP. URL-адреса всегда рассматриваются относительно корня документа приложения, поэтому набор тестов является переносимым и может выполняться без изменений или конфигурации на разных компьютерах (без запуска какого-либо экземпляра Apache).

Это базовый тест, который проверяет, что страница загружена (по умолчанию через GET), и содержит некоторый текст в определенных тегах, указанных с помощью селекторов CSS:

class Example_CrudTest extends Example_AbstractTest
{
public function testFactoryIsLoaded()
{
$this->dispatch('/naked-php/view/type/service/object/Example_Model_PlaceFactory');
$this->assertQueryContentContains('#methods a', 'createCity');
$this->assertQueryContentContains('#methods a', 'createPlaceCategory');
$this->assertQueryContentContains('#methods a', 'createPlace');
$this->assertNotQuery('#object .button.edit');
$this->assertNotQuery('#object .button.remove');
}
}

Даже запросы Ajax можно моделировать с помощью пользовательского заголовка X-Requested-With, распознаваемого стеком MVC. Например, этот тест проверяет, что когда страница включена с помощью Ajax-вызовов, она по-прежнему вызывает правильное действие, но результат не содержит макет:

class Otk_Content_SectionControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
{
    public function testIntegration()
    {
        $this->request->setMethod('GET')
                      ->setHeader('X-Requested-With', 'XMLHttpRequest');
        $this->dispatch("/content/article/{$articleSlug}/add?format=html");
        $this->assertModule('content');
        $this->assertController('article');
        $this->assertQuery('form');
        $this->assertNotQuery('div#container');
    }
}

Конечно, мы также можем имитировать POST-запросы и проверять, что после успешного выполнения они перенаправляют на страницу результатов:

    public function testCityFactoryMethodCreatesCityInstance()
{
$this->getRequest()
->setMethod('POST')
->setPost(array(
'name' => 'New York'
));
$this->dispatch('/naked-php/call/type/service/object/Example_Model_PlaceFactory/method/createCity');
$this->assertRedirectTo('/naked-php/view/type/entity/object/1');
}

Однако есть некоторые ошибки в использовании Zend_Test. Во-первых, выполнение тестовых методов изолированно, как и все тесты PHPUnit. Сеанс сбрасывается вместе с другими компонентами в setUp (), поэтому, например, аутентификация не будет поддерживаться в других методах тестирования. Чтобы выполнить несколько запросов в одном и том же методе тестирования, например, при добавлении объекта и проверке его перечисления на другой странице, не забудьте сбросить запрос и ответ перед отправкой другого действия:

$this->resetRequest()
->resetResponse();

Конечно, вы также можете обрезать базу данных или заново создать фиксаторы в методе setUp (), но убедитесь, что вы вызываете parent :: setUp (), чтобы использовать исходный тестовый пример.

Другая возможная проблема заключается в том, что фронт-контроллер настроен таким образом, что он не будет генерировать исключения: исключения будут отображаться на странице ошибок, а не всплывать непосредственно в PHPUnit. Чтобы избежать такого поведения, вы можете добавить:

$this->frontController->throwExceptions(true);

в ваш метод setUp (). Типичный setUp () выглядит так:

abstract class Example_AbstractTest extends Zend_Test_PHPUnit_ControllerTestCase
{
public function setUp()
{
$application = new Zend_Application(
'testing',
APPLICATION_PATH . '/configs/application.ini'
);
$this->bootstrap = array($application, 'bootstrap');
parent::setUp();
$this->frontController->setParam('bootstrap', $application->getBootstrap());
$this->frontController->throwExceptions(true);
}
}

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

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