Яндекс.Метрика

    Песочница

    Автоматическое тестирование в PHP

    Работа по TDD имеет очевидные преимущества: у разработчика всегда есть чётко описанная в виде теста цель, и он сразу узнает, когда она будет достигнута.
    Тем не менее, есть и некоторые издержки: необходимо постоянно запускать один и тот же тест при изменениях в нем или в соответствующем классе, чтобы не пропустить тот самый момент истины. Вроде бы не такая уж и большая проблема, но постоянное переключение в консоль для проверки сделанных изменений на работоспособность, да и вообще помнить о необходимости этих манипуляций — лишнее рассеивание внимания.

    Далее о том, как все это дело автоматизировать.

    Почему PHP?

    Коллега, занимающийся разработкой на PHP по TDD, искал возможность автоматизировать запуск тестов для только что измененного класса, однако не смог найти готовой системы, удовлетворяющей его нужды, и предложил мне самому создать что-то подобное, тем более, что для других языков такое уже есть и даже в нескольких вариантах. “Почему бы и нет?” — подумал я, и теперь представляю вам результат своего труда.

    Как это выглядит?

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





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

    Что для этого нужно?

    Система состоит из двух приложений autotestd и cautotest.

    autotestd выполняет следующие функции:
    • сопоставляет имена классов с файлами их реализации и файлами тестов
    • следит за изменениями файлов
    • запускает тесты и выводит сообщения с результатом
    • принимает команды управления проектами


    cautotest (console autotest) — консольный клиент для autotestd — позволяет отправлять команды управления проектами:
    • получение информации об активных проектах
    • добавление нового проекта
    • изменение проекта
    • удаление проекта


    autotestd

    Первым делом нужно скачать autotestd по этой ссылке.

    Так как это самая первая версия программы и собирать deb-пакет еще не было смысла, я решил пока ограничиться самописным скриптом установки — install (лежит в архиве с программой):
    #!/bin/bash
    
    apt-get install python2.6 python-sqlalchemy sqlite3 inotify-tools python-pyinotify python-dbus libnotify-bin python-notify
    
    mkdir /etc/autotestd
    cp config.ini /etc/autotestd/
    chmod a+w /etc/autotestd
    
    mkdir /usr/share/autotestd
    cp ./*.py /usr/share/autotestd/
    
    ln -s /usr/share/autotestd/autotestd.py /usr/local/bin/autotestd
    


    Как видите, сначала производится установка всех зависимостей, потом — создание директорий, копирование файлов и, наконец, создание ссылки для запуска приложения.

    Скрипт запускать только от sudo: sudo ./install
    После установки можно запустить само приложение (желательно в фоне): autotestd &

    (В следующей версии планирую сделать autotestd настоящим демоном)

    cautotest

    Теперь скачиваем клиент cautotest
    Установка производится аналогичной командой: sudo ./install
    #!/bin/bash
    
    apt-get install python2.6 python-dbus
    
    mkdir /usr/share/cautotest
    cp ./*.py /usr/share/cautotest/
    
    ln -s /usr/share/cautotest/main.py /usr/local/bin/cautotest
    


    Тут все то же самое.
    Команда cautotest -h выведет справку по параметрам.

    Это все, что требуется для начала работы. Теперь переходим в директорию проекта и выполняюем следующую команду:
    cautotest -a project_name ./application/modules ./tests
    • -a — параметр, указывающий, что мы добавляем проект
    • project_name — название проекта, под которым он будет зарегистрирован в autotestd
    • ./application/modules — путь к директории, содержащей классы Вашего приложения (поиск классов производится рекурсивно, начиная от этой директории)
    • ./tests — путь к директории, содержащей тесты


    ВАЖНО! Сопоставление классов приложения и тестов производится по имени класса:

    //MyClass.php
    class MyClass extends XXX {
    };
    
    //MyClassTest.php
    class MyClassTest extends UnitTest {
    };
    


    Такое именование принято у нас на работе. Если у Вас оно отличается, Вы можете легко это поправить, изменив регулярные выражения, по которым производится сопоставление в файле /etc/autotestd/config.ini.

    На этом вся подготовка заканчивается.

    Команда cautotest -l (“л”) выведет что-то подобное:
    hdg700@laptop:~$ cautotest -l
    Active projects:
    - testp
    Use '-i project_name' for more information


    Дополнительную информацию можно получить командой cautotest -i testp:
    hdg700@laptop:~$ cautotest -i testp
    Project: testp
    Code dir: /home/hdg700/work/testproj/code/
    Test dir: /home/hdg700/work/testproj/test/
    Classes: 1
    Tests: 1


    Теперь попробуйте изменить любой класс Вашего проекта. При сохранении будет запущен соответствующий тест и показано сообщение.

    Как это работает?

    Оба приложения написаны на языке Python2.6.

    Для отслеживания изменений файлов используется inotify и, соответственно, связка для питона: python-pyinotify.

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

    Система вызывает phpunit с указанием конкретного файла для проведения тестов. Команду можно подправить и добавить нужные аргументы в конфиге программы.

    Для хранения информации о проектах используется sqlite3, для связи с sqlite используется sqlalchemy.

    Что интересного выяснилось во время работы?

    1. Запуск внешних программ: если не перехватить stdin, stdout и stderr запускаемой программы, то, когда Ваше приложение запущено в фоновом режиме (autotest &), его выполнение стопорится на запуске внешней программы до тех пор, пока Вы не сделаете fg. Эта особенность изрядно потрепала мне нервы, но в итоге оказалось все логично: достаточно лишь перехватить stdin, stdout и stderr.
    2. Session-шина DBus работает в окружении одного пользователя, поэтому использующие её приложения должны быть запущены от одного пользователя (тавтология для усиления внимания :). При переводе autotestd в режим демона надо будет использовать system-bus.


    PS

    Вы ознакомились с первой версией автотеста для PHP. Надеюсь, кому-то она пригодится и скрасит серые будни. :)
    Тех, кому захочется увидеть какой-то дополнительный функционал, либо пожаловаться на баги, прошу писать в разделы issues на githab’е: autotestd-issues и cautotest-issues.

    И последнее. Добавляйтесь в watchers к проектам: так Вы сможете получать уведомления об изменениях, а я увижу, стоит ли развивать их (проекты) дальше.

    Спасибо за внимание.