Протестировать класс, вызывающий header(), Simple Test-ом

Lightning

Трудоголик
Протестировать класс, вызывающий header(), Simple Test-ом

Совсем недавно использую Simple Test. Столкнулся с такой проблемой: при тестировании класса, в одном методе которого вызывается функция header(), получаю Exception (headers already sent). Пробовал делать буферизацию всего вывода - почему-то не помогает. Нигде не нашел описания данной проблемы. Как можно протестировать классы, которые что-либо пишут в заголовки?
 

fixxxer

К.О.
Партнер клуба
PHP:
class Response {
    protected $headers = array();
    protected $body = '';
    public funciton addHeader($header) {
        $this->headers[] = $header;
    }
    public function addBody($s) {
        $this->body .= $s;
    }
    public function send() {
        foreach ($this->headers as $h) header($h);
        print $this->body;
        $this->clear();
    }
    public function clear() {
        $this->headers = array();
        $this->body = '';
    }
}
Для тестов добавляешь геттеры (ну или моками) - и тестируй на здоровье. :) А напрямую через header/print выводить - не комильфо =)

Заодно разгребешь ответственности: как только понадобиться передать экземпляр Response в неожиданное место, подумай, а должен ли этот класс писать в Response.
 

Lightning

Трудоголик
fixxxer
Мне нужно как раз протестировать похожий класс. Только он у меня отвечает только за header-ы, причем он предоставляет сразу готовые методы типа no_cache() (для вывода заголовка отмены кеширования), redirect($url) (для вывода заголовка Location для редиректа) и т.д. И я его не передаю в неожиданные места) Он у меня синглтон.
А тесты-то для него нужны. Или ты предлагаешь мне создать еще один класс, который будет содержать только add_header() и send() ?
 

fixxxer

К.О.
Партнер клуба
Ох уж эта мания тестирования всего подряд. Ну, я не знаю, выдели лоу левел методы такие как отправка header() в отдельные методы и вешай на них моки. Хотя целесообразность тестирования такого примитива вообще под сомнением.
 

Lightning

Трудоголик
Ну, я не знаю, выдели лоу левел методы такие как отправка header() в отдельные методы и вешай на них моки.
Спасибо. Так и сделаю.
Ох уж эта мания тестирования всего подряд.
В TDD я - новичок. Прочитал только книгу Кента Бека "Экстремальное программирование. Тестирование". Вполне возможно я многое делаю неправильно. Может дадите какие-нибудь ссылки на материалы, где пишут как и что тестировать?
 

Lightning

Трудоголик
Ня) Может стоит задаться, изначально, вопросом зачем?
Что зачем? тестировать?
Затем чтобы:
1) Повысить надежность и качество кода
2) Повысить свою уверенность в своем коде
3) Находить ошибки сразу же, как они появляются
 

fixxxer

К.О.
Партнер клуба
Во всем нужна мера. Тестить

class HelloWorld {
static function outputGreeting() {
echo "Hello World\n";
}
}

нет никакого смысла.

Если хочешь тестировать client side - Selenium в помощь
 

fixxxer

К.О.
Партнер клуба
а у меня вот не апач, мне что делать?

тдд часто и отталкивает пропагандой тупого следования правилам, даже когда они неуместны. давайте тестировать, правильно ли в php работает echo. или оператор конкатенации.
 

zerkms

TDD infected
Команда форума
fixxxer
давайте тестировать, правильно ли в php работает echo. или оператор конкатенации.
это ты уже утрируешь. класс может отправлять разные заголовки в зависимости от условий.

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

fixxxer

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

А echo/header, раскиданные по 10 классам, это уже признак говнокода
 

Lightning

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

А echo/header, раскиданные по 10 классам, это уже признак говнокода
Я не раскидываю echo/header по 10 классам, но использование такого класса Response в моем случае не целесообразно. Один класс отвечает за header-ы, в нем присутсвует вызов header(); Один класс отвечает за вывод, в нем присутствует вызов echo, но только в одном методе. От второго класса есть потомки. Кроме того класс, отвечающий за header-ы - синглтон. Нет смысла создавать еще класс Response и действовать через него.

А как насчет session_start()?
Она тоже отправляет заголовки. У меня session_start() находиться только в одном классе и я из-за этого не могу его протестировать. Предложишь сгрузить echo(), header(), session_start(), set_cookie() в один класс Response?

-~{}~ 02.03.09 22:48:

fixxxer
Кстати, а разве классу Response не следует быть синглтоном? Ведь в приложении может быть только один респонс, я не прав?
 

whirlwind

TDD infected, paranoid
Lightning

Делай врапперы на пхпэшные функции и вешай на них моки. Это нормальная практика "золотой середины".

-~{}~ 03.03.09 17:01:

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

Lightning

Трудоголик
Делай врапперы на пхпэшные функции и вешай на них моки. Это нормальная практика "золотой середины".
Создавать класс, который будет врапить echo(), header(), session_start(), set_cookie(), которые и так встречаются в коде один раз - это разве хорошо? Это - лишняя косвенность. Мне кажется, что это попахивает какой-то подстройкой под тестирование.
Кроме того, я использую враперы, когда необходимо, но не всегда могу повесить на них моки. Не буду же я передавать объекту используемые им враперы при его создании, так как это ведет к дублированию. Поэтому я делаю в классе методы, которые создают используемые враперы. В этом случае, я еще могу повесить моки, правда это не очень красиво выглядит (нужно сделать частичный мок тестируемого класса, в котором эмулировать метод создающий враперы, чтобы он возвращал их моки). Однако, если одни и те же враперы используются различными классами, находящимися в различных иерархиях, то я уже не могу прибегнуть к данному приему и поэтому использую фабрику враперов. В этом случае я обращаюсь к методам фабрики статически (или делаю фабрику синглтоном), так как было бы не логично создавать множество экземпляров фабрики. А так как есть статические вызовы, я не могу повесить моки.

-~{}~ 04.03.09 14:09:

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

-~{}~ 04.03.09 14:21:

[offtop]
HttpResponse
ConsoleResponse
XmlResponse
А зачем разделять XmlResponse и HttpResponse?
[/offtop]
 
Сверху