PHPUnit+Netbeans

    Здравствуйте.

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

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

    В крупных конторах для этого есть специальные люди, которые занимаются тем, что пишут unit-тесты.

    Меньшие конторы, фрилансеры этим грешат, да и на маленьких проектах это не всегда нужно. Согласитесь, если класс на 100 строк, то писать тесты на 200 строк кажется лишней тратой времени.

    Тем не менее, не буду вдаваться в подробности кому надо кому нет.

    Я хочу показать как можно клево тестировать код на NetBeans с удобным GUI.


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

    Итак, в среде NetBeans начиная, кажется с 6.7 добавили возможность тестирование кода… Точную версию не помню — посмотрите на сайте бинса.

    Создание тест-класса осуществляется в пару кликов.

    Но, все по порядку.

    Для того чтобы тестировать, нам необходимо:
    1. Собственно NetBeans
    2. PHPUnit
    3. Желание разобраться

    Установка PHPUnit


    Установка PHPUnit идет через PEAR.

    Мануал здесь.


    Думаю проблем с этим не будет.

    Перейдем к созданию класса, который будем тестировать.


    А тестировать мы будем калькулятор. Да-да, классический примерчик.

    Итак класс будет иметь вид:
    <?php
    
    class calc {
        
        /**
         * Хранит результат последней операции.
         * 
         * @var Double
         */
        protected $last;
    
        /**
         * Суммирует два числа.
         *
         * @param Double $a
         * @param Double $b
         * @assert (1,2) == 3
         * @assert (10,10) == 20
         * @assert (1,0) == 1
         * @assert (0,0) == 0
         */
        public function sum($a, $b)
        {
            $this->last = $a+$b;
            return $this->getLast();
        }
    
        /**
         * Делит число $a на число $b
         *
         * @param Double $a
         * @param Double $b
         * @return Double
         * @assert (6,3) == 2
         * @assert (10,5) == 2
         * @assert (15,3) == 5
         * @assert (15,0) == 0
         */
        public function div($a,$b)
        {
            if ( $b==0) throw new Exception("Division by zero");
            $this->last = $a/$b;
            return $this->getLast();
        }
    
        /**
         * Возвращает результат последней операции
         *
         * @return Double
         */
        public function getLast()
        {
            return $this->last;
        }
    
        /**
         * Вычитание
         *
         * @param double $a
         * @param double $b
         * @return double
         * @assert (1,2) == -1
         * @assert (10,2) == 8
         * @assert (15,2) == 13
         *
         */
        public function minus($a, $b)
        {
            $this->last = $a-$b;
            if($a<$b) $this->last=0; // Вот такая ошибка у нас будет (!!)
            return $this->getLast();
        }
    
    
        /**
         *
         * @param double $a
         * @param double $b
         * @return double
         * @assert (10,20) == 200
         * @assert (1,20) == 20
         * @assert (4,-3) == -12
         * @assert (10,0) == 0
         */
        public function umnojenie($a,$b)
        {
    
            $this->last = $a*$b;
            if( $this->last == 0 ) $this->last=1; // ОШИБКА
            return $this->getLast();
        }
    
    
        /**
         * Возведение в степень
         *
         * @param double $a
         * @param double $b
         * @assert (1,2) == 1
         * @assert (3,2) == 9
         * @assert (10,-1) == 0.1
         * @assert (10,0) == 1
         * @assert (0,0) == 0
         */
        public function power($a, $b)
        {
            $this->last = pow($a,$b);
        }
    }
    ?>

    В этом классе у нас описаны операции.
    Документированный в стиле phpdoc.

    В данном классе допущены специально.
    В частности метод pow не возвращает значение. Забыли про return.

    Что такое assert ?
    assert позволяет заранее обозначит что мы подаем на вход и что хотим получить на выход.
    То есть PHPUnit при создании тестируемого класса создаст для каждого такого assert свой тест-функцию.
    Удобно — экономим время.

    Переходим к созданию тест-класса


    Теперь необходимо создать теск-класс.
    Среда NetBeans позволяет это сделать в 2 клика(!)


    С первого раза у вас спросят куда сохранять класс.
    Выберите папку в проекте, у меня например это папка test.
    Далее у вас откроется окно с созданным классом.
    По сколько использовался assert методов получилось столько сколько на картинке:


    Все тестирующие методы имеют префикс «test».
    Методы setUp, tearDown вызываются до и после запуска каждого теста!
    То есть в них можно подключатся к чему либо или еще что-то делать, в общем все что связано для успешной работы.

    Запускаем SHIFT+F6 тест.
    Должно получиться нечто как показано на картинке.


    Получается каждый тест выполнен и 60% успешных.

    Видно, что все тесты power FAILED, потому что нет return.
    Исправляем.
        public function power($a, $b)
        {
            $this->last = pow($a,$b);
            return $this->getLast();
        }
    


    Получаем 80% успешных тестов.


    Исправляем полученные ошибки.
    testDiv4 — выдает Exception который не ловится. Надо в тесте это словить.
    Для этого перед методом public function testDiv4
    необходимо добавить
    @expectedException аннотацию.

    то есть мы пишем так:
    /**
         * Generated from @assert (15,0) == 0.
         * @expectedException Exception
         */
        public function testDiv4()
        {
    
    
            $this->assertEquals(
              0,
              $this->object->div(15,0)
            );
        }
    


    Таким образом мы сообщаем PHPUnit что мы ожидаем Exception.

    Далее функция testMinus, напомню мы насильно запретили возвращает ответ меньше 0.
    Исправляем это.

    Тоже самое делаем с методом umnojenie.
        /**
         * Вычитание
         *
         * @param double $a
         * @param double $b
         * @return double
         * @assert (1,2) == -1
         * @assert (10,2) == 8
         * @assert (15,2) == 13
         *
         */
        public function minus($a, $b)
        {
            $this->last = $a-$b;
            
            return $this->getLast();
        }
    
    
        /**
         *
         * @param double $a
         * @param double $b
         * @return double
         * @assert (10,20) == 200
         * @assert (1,20) == 20
         * @assert (4,-3) == -12
         * @assert (10,0) == 0
         */
        public function umnojenie($a,$b)
        {
    
            $this->last = $a*$b;
          
            return $this->getLast();
        }
    


    то есть теперь должно пройти тест.

    Запускаем заново.
    После запуска у вас будет 95% PASSED.

    Последние 4 процентов уходят на тест testPower5.
    Почему? Потому что при написанни assert ошибся
    и получилось что pow(0,0)==1 а мы ожидаем 0.
    По этому меняем на 1 и запускам.

    20 TESTS PASSED.
    100% PASSED.

    Все отлично!

    На последок напишем пример:
    5*5+5=30.

    public function testClass()
        {
            $this->assertEquals(30, $this->object->sum( $this->object->umnojenie(5, 5), 5));
        }

    Запускаем ОК.

    Какие еще есть проверки


    В данном примере использоваться примитивный assertEquals.
    Но есть еще много разных методов.

    Вот здесь есть краткое описание на русском: habrahabr.ru/blogs/php/56289

    — В след раз будет сделан обзор более сложного примера, работа с бд, файлами.

    Статья в моем блоге — anton.in.ua/2009/09/18/phpunit-netbeans

    Всем спасибо.
    Поделиться публикацией
    Похожие публикации
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 40
    • –3
      Не хочу показаться навязчивым, но кат бы здесь не помешал…
      • –1
        А ведь бы ло же мнение… Когда писал коммент — катом даже и не пахло. Ну да ладно ))
      • +1
        >>В крупных конторах для этого есть специальные люди, которые занимаются тем, что пишут unit-тесты.
        _очень_ спорное замечание
        • +2
          За что заминусовали человека?
          Речь идет о модульных тестах, а не о каких-то других.
          Модульные тесты должны писать те же программисты, которые пишут сам рабочий код, иначе в проекте будет полная каша.
          Неужели про test-driven development никогда не слышали?
          • +1
            дык очевидно же =) привыкайте. Есть особо ранимые группы, в которых нельзя сделать замечание, даже по делу. Ну и всех кто не согласен — минусуют, без разбора.
        • –8
          Это всё описано в мануале к NetBeans, расскажите лучше о чем-нибудь посложнее :)
          • +1
            >Не для кого не секрет

            Исправьте пожалуйста. Не -> Ни
            Я не грамотей, но такие ошибки\опечатки глаз режут.
            • –3
              Вы ТОЧНО не грамотей.
              • НЛО прилетело и опубликовало эту надпись здесь
                • +1
                  Блин, да. И Вы тоже:) А я неправ, почему-то ркшил, что речь о втором «не», а первое проглядел.

                  Прошу прощения.
            • +5
              а мне понравилось. просто и доступно. только покоробило umnojenie($a,$b) — если все остальное по английски, почему бы не написать mult($a,$b)?
              • –3
                потому что просто так вот повезло и по русски и по английски плюс и минус одинаково пишутся. Только вот правильнее было называть add и sub вместо plus и minus, ибо plus ну никак не переводится как сложить или сложение.
                • +1
                  ну там же есть функция div вместо delenie… ну да это флуд оффтоп и флуд. спасибо за статью!
              • +2
                Да, на самом деле не плохо, спасибо, пользуюсь netbeans, а об этом не знал.
                • –1
                  В крупных конторах для этого есть специальные люди, которые занимаются тем, что пишут…

                  спецификации. тесты это переложение спецификаций в программный код.
                  Согласитесь, если класс на 100 строк, то писать тесты на 200 строк кажется лишней тратой времени

                  Часто встречаются штуки, которые проще написать, чем описать. Кстати, пример калькулятора из их числа. :)
                  * @assert (15,0) == 0.
                  */
                  public function div($a,$b)



                  /**
                  * Generated from @assert (15,0) == 0.
                  * @expectedException Exception
                  */
                  public function testDiv4()

                  Вот это полная хрень. Если в спецификации написано «assert (15,0) == 0», значит функция div при аргументах 15 и 0 _обязана_ вернуть 0. И Netbeans генерировал правильный код теста. Но Вы, вместо того, чтобы исправить реализацию, всех обманули, переделав сам тест.

                  Условие неверных спецификаций не рассматриваем, по очевидным причинам.
                  • 0
                    На самом деле генерирует на основании моего кода.

                    Я в классе сделал так, что деление на 0 дает эксепшн. Вы если хотите возвращайте ноль.

                    @assert (15,0) == 0.
                    Это не вынуждает, я ожидал ексепшн. А он будет независимо от того что я жду: 0, 10 или 100.

                    Спасибо за то что прочитали статью
                  • 0
                    Гораздо лучше, когда тестирование прикручено к сайту. Что-то типо такого
                    • 0
                      ну с параметрами типа int всё ясно, а что делать не для Hello-world классов? Если там объект надо передать в метод?
                      Тут уже не подойодет походу такой способ.
                      • 0
                        К сожалению в PHPUnit 3.4 нельзя использовать _ (подчеркивание) в именах классов.
                        Это малость напрягает.
                        Приходится переименовывать тестовый файл, но тогда его не видит IDE при запуске из меню.
                        А версия 4.0 где это можно требует PHP 5.3.
                        • 0
                          Всё проще, в ночной сборке NetBeans есть возможность указать файл bootstrap, в котором уже можно разруливать __autoload по своему, подключаться к базе и выполнять прочие действия.

                          blogs.sun.com/netbeansphp/entry/recent_improvements_in_phpunit_support
                          • 0
                            Ну тогда будем ждать стабильного билда.\
                            Хотя как только я это заметил сразу написал тикет в поддержку. Мне ответили что это поведение юнита и ничего они с этим сделать не могут.
                            На данный момент мне проще использовать SimpleTest.
                          • 0
                            www.phpunit.de/manual/current/en/textui.html ищем ключ --bootstrap www.phpunit.de/manual/current/en/appendixes.configuration.html ищем атрибут bootstrap="/path/to/bootstrap.php"
                          • +1
                            Спасибо за статью! Обычно пренебрегал такими тестами и все проверял руками. Начну внедрять это в свои проекты.
                            • 0
                              Очень хотел бы пример как тестить всякие формы и приложения с БД.
                              • 0
                                будет, но позже
                              • 0
                                Именно-именно то, что мне сейчас необходимо!
                                Огромное Вам программерское спасибище! =)
                                • 0
                                  Отлично, спасибо!

                                  По опыту, у Zend Studio время запуска одного теста составляет около 10-20 секунд, что очень много. А здесь как?

                                  Добавил бы еще, что согласно идеологии TDD, подразумевается реализовывать не весь класс сразу, после чего запускать тестирование, но итеративно разрабатывать по методу, следуя мантре Green-Red-Refactor. Более того, сначала должны быть тесты, а затем реализация. Впрочем, вы о TDD и не говорили, это я так, к слову.
                                  • НЛО прилетело и опубликовало эту надпись здесь
                                    • 0
                                      Он у вас уже должен быть установлен через PEAR.
                                      www.phpunit.de/manual/current/en/installation.html

                                      После чего надо в Tools -> Options -> PHP в поле PHPUnit script прописать путь до phpunit.bat
                                      ( на пример D:\xampp\php\phpunit.bat ).

                                      После чего можно начинать использовать. При первом запуске нетбинс предложит выбрать папку для тестовых файлов.
                                      blogs.sun.com/netbeansphp/entry/phpunit_support_added
                                      • 0
                                        Да, именно так.

                                        я не уделил внимание установке netbeans и phpunit так как полагаю, что человек который немного шарит веб не новичек за компом :)
                                        • НЛО прилетело и опубликовало эту надпись здесь
                                        • НЛО прилетело и опубликовало эту надпись здесь
                                          • 0
                                            Просто нужно было сделать pear upgrade --force PEAR ;)
                                            Помогает.
                                            • НЛО прилетело и опубликовало эту надпись здесь
                                      • +1
                                        плюс к сложным валидациям форм и работам с БД хотелось бы почитать про тесты для ajax приложений
                                        • 0
                                          Эх, всем хорош PHP в NetBeans, но отсутствие аналога Remote Systems в Eclipse для меня непреодолимое препятствие. Иначе давно бы перешёл, NetBeans и удобнее, и быстрее.
                                          • НЛО прилетело и опубликовало эту надпись здесь
                                          • 0
                                            Извиняюсь за позднее зажигание, но может кто-то прикручивал юнит тестирование к codeigniter и подобным фреймворкам. Дело в том что у большинства фреймворков динамическая инициализация классов, то есть чтобы загрузить определенный класс модели мы пишем $this->load->model('your_model'). В тоже время в файле с классом модели мы не пишем include (или require) ('path/to/root_model_class');
                                            В итоге при генерации тестовых классов phpunit ругается. Простое добавление инклуда проблему не решило. Глубоко ковыряться все руки недоходят. Может кто то уже решил данную проблему?
                                            • 0
                                              Возникла необходимость запускать юнит тесты на сервере, не покидая при этом NetBeans (работающую под Windows). Готового решения не нашел, поэтому опишу здесь свое — может, сэкономит кому-нибудь время.

                                              1. Скачиваем putty, plink и pscp отсюда, кладем их в одну папку.
                                              2. В putty сохраняем сессию, которая будет использоваться для подключения к серверу (назовем ее unitTestSsh).
                                              3. Изменяем файл phpunit.bat

                                              phpunit.bat
                                              @echo off
                                              cd C:\Path\To\Putty
                                              plink unitTestSsh php /path/to/script/on/server/runRemotePhpUnit.php 
                                              pscp unitTestSsh:/path/to/script/on/server/log.xml C:\Users\UserName\AppData\Local\Temp\nb-phpunit-log.xml
                                              pscp unitTestSsh:/path/to/script/on/server/output.txt C:\Users\UserName\AppData\Local\Temp\nb-phpunit-output.txt
                                              type C:\Users\UserName\AppData\Local\Temp\nb-phpunit-output.txt
                                              


                                              4. Создаем скрипт runRemotePhpUnit.php на сервере

                                              runRemotePhpUnit.php
                                              $logFpath = dirname(__FILE__) . '/log.xml';
                                              $outputFpath = dirname(__FILE__) . '/output.txt';
                                              $bootstrapFpath = dirname(__FILE__) . '/bootstrap.php';
                                              $unitTestsPath = dirname(__FILE__);
                                              $remotePath = '/path/to/tests/on/server';
                                              $localPath = 'C:/path/to/tests/on/localPc';
                                              
                                              $output = shell_exec("phpunit --colors --log-junit $logFpath --bootstrap $bootstrapFpath $unitTestsPath");
                                              $output = str_replace($remotePath, $localPath, $output);
                                              file_put_contents($outputFpath, $output);
                                              
                                              $log = file_get_contents($logFpath);
                                              $log = str_replace($remotePath, $localPath, $log);
                                              file_put_contents($logFpath, $log);
                                              
                                              


                                              Кода не много, но поясню, что мы сделали.

                                              При нажатии на кнопку «Test» NetBeans запускает файл phpunit.bat. Этот файл в свою очередь запускает утилиту phpunit, которая прогоняет тесты и записывает результат в xml-файл. NetBeans подхватывает xml-файл, парсит и выводит красочные результаты в окошке «Test Results».

                                              Мы же заставили phpunit.bat запускать не локально установленную утилиту phpunit, а php-скрипт на сервере (при помощи plink). Этот php-скрипт запускает phpunit и сохраняет вывод в файл, предварительно обрабатывая его (заменяя пути). Далее наш phpunit.bat копирует xml-файл с сервера на локальную машину (с помощью pscp) и NetBeans его радостно подхватывает.

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