Pull to refresh

Comments 27

Ради таких твитов стоит жить и работать, да.
Классно. Я писал патч для issue10, отправил его на review, но ответа так и не получил. У вас проблем с выборочным запуском тестов нет? Не работаете над этим issue?

Как вам код phpunit? На мой взгляд очень тяжелый для понимания-рефакторинга
Спасибо за вопросы!

Нет, над issue10 мы не работали. Проблем с выборочным запуском тестов так же не имели. --filter вполне себе справляется с нашими задачами.

По поводу понятности кода phpunit — тоже особых проблем не испытывали. Код замечательно читается и дорабатывается + хорошая документация. С чем именно у вас были проблемы? Уверен что сообщество поможет ответить на ваши вопросы.
Да, "--filter вполне себе справляется с нашими задачами", но суть в том что он при этом начинает дергать классы, методы, dataProvider'ы, которые точно под этот filter не попадают. Вот примерный тест, не знаю насколько он сейчас актуален: github.com/ewgRa/phpunit/commit/5b12236856d3c139b37ebf9a55d76733e8c08554

В итоге допустим в Zend Framework (или Symphony, не помню точно) запускать тесты с --filter то еще удовольствие. Они в dataProvider запихивают достаточно емкую работу и все эти dataProvider будут выполнятся, даже если методы, которые фильтруются не учавствуют в тестах.

> С чем именно у вас были проблемы?
Я уже точно не помню, но там мне показалась архитектура, которая явно строится по принципу дерева от крупного к мелкому или тому подобное реализована очень, очень сложно. В итоге как-то это рефакторить было очень проблематично. Мне кажется то, что это issue уже три года как не закрывается именно из-за архитектуры и того кода, кторый там реализован, уж слишком сложно он поддается рефакторингу.
Я понимаю о чем вы говорите. Действительно, с этой точки зрения архитектура выглядит не очень оптимальной.
Мы, чтобы не натыкаться на подобные подводные камни, используем простое правило в нашем проекте — «data provider'ы должны возвращать только статичные данные». Никаких объектов, никакой хитрой логики. Если нужны различные объекты в тестах, то в датапровайдерах надо возвращать правила или параметры для создания таких объектов. А сами объекты надо создавать уже в тестах или setUp'е и очищать в tearDown'е.
Это, конечно, не исключает сбор провайдеров при запуске тестов с фильтром, но существенно облегчает этот процесс.
Более того, то, что провайдеры инициализируются задолго до выполнения тестов ведет к еще одной проблеме — нарушению изоляции. Объект, созданный перед всем набором тестов может быть изменен каким-нибудь тестом до нужного и в нужный тест объект может прийти измененным.
Это еще одна причина, почему мы запрещаем инициализацию объектов в провайдерах.
То же самое справедливо и для некоторых других методов фреймворка, вы совершенно правы.
Для автоматического контроля ситуации мы даже добавили в нашу запускалку тестов специальный тест, который проверяет состояние объектов между тестами — «SanityCheck». Правило + автоматическая проверка спасают :-).
При вашем количестве тестов, даже такие «пустые» dataProvider'ы должны неплохо замедлять выполнение тестов с --filter + расход памяти (если я правильно помню там все эти результаты dataProvider складываются в память)

С одной стороны я рад за вас, с другой жаль :) Может если бы для вас это было проблемой, ваших ресурсов хватило бы, чтобы сдвинуть это дело с мертвой точки.
А можно ли вообще отказаться от data provider'ов? Мне кажется они не добавляют читаемости в тест, а скорее наоборот.

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

На «тяжелые» провайдеры зенда и симфони тоже бы с удовольствием посмотрел. При беглом осмотре кода симфони я увидел только статичные данные в провайдерах. Может переписали уже?
К сожалению сейчас не могу сказать, давно дело было.
К сожалению не могу, ZF2 требует phpunit 3.7, запустить тесты посмотреть не могу. Дело давно было, с тех пор я не касался этого вопроса.
Добавляя дополнительные наборы данных можно увеличить покрытие кода без написания нового теста.
>>> но суть в том что он при этом начинает дергать классы, методы, dataProvider'ы, которые точно под этот filter не попадают

--filter='/::FULL_CLASS_OR_METHOD_NAME( .*)?$/' /path/to/TestFile.php


Для PHPStorm я сделал External Tool в настройках, который по хоткею запускает тесты в dev-системе (линукс через plink.exe), фильтруя по выделенному мышкой методу или классу. Самая длинная строка настройки (не считая пути к плинку):

-load PUTTY_PROFILE_NAME -pw PASSWORD phpunit --verbose --bootstrap=/path/to/Bootstrap.php --filter='/::$SelectedText$( .*)?$/' /path/to/document/root/$/FileRelativePath$
Ну как вариант, да
В PHPUnit из коробки есть 4 возможности выборочного запуска тестов, подробнее в другом комментарии.
Вы не перестаете радовать отличными постами по QA, это «свежие глотки воздуха» в пустоте этой тематике хабра. Прочитал с удовольствием. Задумываетесь ли Вы об особенностях этого линейного механизма подсчета покрытия. По факту это все крутится около «вызвалась строка — не вызвалась». Тематика ухода к более интеллектуальному подсчету CC очень интересна.
PS Улучшать что-то в общее благо это неоспоримо ЗДОРОВО!
Спасибо!
Да, вы правы — простой подсчет покрытия по строкам, которые были вызваны, это не панацея. Даже при огромном покрытии кода тестами нельзя считать что все хорошо и быть на 100% уверенным в том что все работает.

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

О более интеллектуальном подсчете покрытия пока не задумывались, но вполне возможно что к этому придем.
Не пробовали. Спасибо за ссылку, очень интересно.
Более интеллектуальное покрытие можно реализовать на основе стандартных возможностей PHPUnit — Appendix B. Annotations — @covers. Позволяет указать что именно стоит учитывать как вызванные строки для каждого конкретного теста, весь остальной код в рамках теста будет считаться не исполненным.
Спасибо, как раз сегодня мы начали прикручивать code coverge и phpconv к нашим распределенным тестам, а тут такой open source подарок :)
Может подскажете, нет ли какой нибудь хитрости чтобы code coverage собирался быстрее? Или же проще дать CI серверу процессор побыстрее?
Быстрее — только многопоточный запуск может помочь. К сожалению с xdebug'ом, собирающим покрытие, тесты идут медленнее, это факт.
Тесты для сбора покрытия мы гоняем на виртуалке с 32 гигабайтами оперативной памяти и 24ю ядрами Intel Xeon 2.93GHz. При этом из упомянутых 2,5 часов процентов 70 времени уходит именно на прогон всех тестов.
Вероятно, скоро мы придем к вопросу оптимизации еще раз, так как количество тестов с каждым днем увеличивается. Если придумаем что-нибудь, обязательно напишем об этом и отдадим в opensource.
Спасибо за ответ. Мы думаем в свободное время попробовать разобраться с Gearman'ом, ведь в наличии есть около сотни девелоперских и обычных офисных компов, если бы удалось запускать паралельно тесты на них, все бы просто летало! :) А вы не пробовали что-то похожее?
Пока не пробовали, но идеи такие давно витают. Де-факто тесты (не для кавериджа, а для задач, билдов и т.д.) мы итак гоняем в облаке. Набор из десятка виртуалок, из которых берется самая свободная и запускаются на ней тесты.
В общем-то такой же принцип можно применить и к девелоперским машинам, но тут возникают проблемы с окружением. Если виртуалки еще более-менее можно продублировать с точки зрения продакшеновских машин — ресурсы, версии софта и библиотек, то с клиентскими машинами такое будет очень сложно провернуть.
Мы думали о том, чтобы поднимать на машинах виртуалки с помощью Vagrant и Chef, таким образом проблема различних окружений решается. Вопрос в том, стоит ли этим заниматься, если проще добавить ресурсы в облако :) Но возможность получить кластер с 100 нодов греет гиковское сердце :)
Success Story

При помощи --group или --fiter разделите существующий набор на части. --group потребует измений в файлах с тестами, --filter — подобрать правильные маски чтобы ничего не потерялось. Когда разделив запуск на несколько потоков вы добьетесь его выполнения (при определенном объеме тестов вы столкнетесь с проблемами не изолированности тестов друг от друга, зависимости от внешних ресурсов и т. п.) вы сможете ускорить процесс в десятки раз, все в конечном итоге упрется в коль-во процессов на которые хватит ресурсов (в основном памяти и процессора, но так же могут быть проблемы со скоростью записи на диск, это зависит от характера тестов) при параллельном запуске. Дальше запускаем сколько нам нужно потоков с помощью простого bash скрипта:

#!/bin/bash -x

# Получаем данные покрытия для первой половины тестов выполняя в фоне
phpunit --group A --coverage-php coverage/data/group_A.cov &

# Получаем данные покрытия для второй половины тестов  выполняя в фоне
phpunit --group B --coverage-php coverage/data/group_B.cov & 

# Ждем пока завершатся оба потока выполняясь параллельно
wait                                                         

# Объединяем даныне покрытия из двух потоков и генерируем HTML
phpcov --merge --html coverage/html coverage/data            


P. S.

Разбить потоки на почти равные части чтобы все они выполнялись примерно одинаковое время можно при помощи Chapter 7. Organizing Tests — Composing a Test Suite Using XML Configuration или же можно не заморачиваться и просто раскидать все по директориям — Chapter 7. Organizing Tests — Composing a Test Suite Using the Filesystem.

P. P. S.

Есть идея расширить возможности PHPUnit и реализовать возможность запуска набора тестов на указанном кол-ве потоков с автоматическим распределением тестов по «свободным» потокам, но это уже совсем другая история.
Проекты с автоматическим распределенным Phpunit уже есть на gihub'е. Проблемы начинаются с появлением интеграционных и функциональных тестов. Например использование БД или файлов. Можно передавать в потоки Phpunit параметры, и с их помощью создавать уникальные БД, файлы, и т. д. но в проектах с фреймворками (SF2, Doctrine) очень трудно все предусмотреть и изменить. Проще(?) всего запускать тесты в параллель на различных виртуалках.
В PHPUnit есть известный bug с покрытием таких конструкций:
$var = false;
if($var == true)
    return false;
if($var == true) return false;

If в данном случае не отработает, однако Xdebug считает его покрытым. Как выход можно заключить операторы после if в фигурные скобки:
$var = false;
if($var == true) {
    return false;
}
if($var == true) { return false; }

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

Вопрос вот в чем, как в badoo справляетесь с этой проблемой?
Sign up to leave a comment.