Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. PHP

Руководство по разработке через тестирование для начинающих

Read Time: 11 mins
This post is part of a series called Test-Driven PHP.
It's Time To Dig In
Test-Driven Development in PHP: First Steps

Russian (Pусский) translation by Sergey Zhuk (you can also view the original English article)

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

Что означает разработка через тестирование?

С начала компьютерной эры программисты и ошибки боролись за превосходство. Это неизбежное явление. Даже величайшие программисты становились жертвой этих аномалий. Не существует безопасного кода. Вот почему мы используем тестирование. Программисты, хотя бы некоторые из них, проверяют свой код, запустив его на своих рабочих машинах, чтобы убедиться, что он делает, что он должен.


Программист, который тестирует свои программы.
Изображение предоставлено http://www.youthedesigner.com

Безумным программист, который не тестирует свои программы.
Изображение предоставлено http://www.internetannoyanceday.com

Разработка через тестирование это техника программирования, которая требует одновременного написания кода программы и тестов для него. Это гарантирует, что ваш код протестирован — и позволяет тестировать ваш код быстро и легко, так как этот процесс автоматизирован.

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

Разработка через тестирование или TDD представляет собой короткий итеративный цикл разработки:

  1. Перед написанием любого кода, сначала необходимо написать тесты для него. При написании автоматических тестов, необходимо принимать во внимание все возможные входные данные, ошибки и выходные данные. При таком подходе ваши мысли не будут привязаны ни к какому коду, который уже написан.
  2. Первый раз, когда вы запускаете автоматические тесты, они завершатся неудачей, обозначая, что ваш код еще не готов.
  3. После этого вы можете приступить к программированию. Если уже существует тест, и есть трестируемый код, но по-прежнему тесты завершаются неудачей, это означает, что код еще не готов. Код может считаться исправленным, если он проходит все проверки.
  4. Как только код успешно проходит тест, можно начать его очищение с помощью рефакторинга. До тех пор пока код успешно проходит тесты, можно считать что он по-прежнему правильно работает. Вам больше нет необходимости беспокоится об изменениях, которые могут привести к багам.
  5. И снова повторяем этот цикл с другим методом или программой.

Отлично, но чем же это лучше обычного тестирования?

Вы когда-нибудь целенаправленно пропускали тестирование программы, потому что:

  • Вы чувствовали, что это будет потерей времени, так как было внесено слишком мало изменений.
  • Вы чувствовали себя ленивым начать тестировать все снова?
  • У вас не было достаточно времени для тестирования, так как менеджер проекта настаивал на скорейшем запуске?
  • Вы обещали себе, протестировать это завтра?
  • Вы предпочли любимое телешоу тестированию?

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

TDD был создан чтобы ликвидировать наши оправдания. Когда программа была разработана с использованием TDD, то мы можем быстро и эффективно применять новые изменения. Все что нам нужно сделать - это запустить автоматические тесты! Если все тесты пройдены, то можно двигаться дальше, если нет - то это означает, что наши изменения что-то сломали. Зная какая именно часть тестов упала, мы можем легко определить, в каком именно месте наших изменений мы сломали код и быстро это исправить.


Я купился. Как мы это сделаем?

Существует множество PHP автоматизированных фреймворков для тестирования, которые мы можем использовать. Наиболее широко используемый из них - PHPUnit.

PHPUnit является довольно мощным фреймворком для тестирования, который может быть легко интегрирован в ваши собственные проекты или другие проекты, построенные поверх популярных PHP фреймворков.

Для наших же целей, нам не нужно то множество функций, которое предлагает PHPUnit. Вместо него мы будем использовать гораздо более простой фреймворк SimpleTest.

В следующих шагах давайте предположим, что мы разрабатываем приложение гостевой книги, где любой пользователь может добавлять и просматривать записи. Предположим, что верстка была завершена, и что мы просто создаем класс, который содержит логику приложения гостевой книги, где приложение вставляет записи и читает их из базы данных. Мы будем разрабатывать и тестировать часть, которая отвечает за чтение.


Шаг 1. Настройка SimpleTest

Это возможно самый простой шаг из всех. Даже этот парень смог бы сделать это:


Я могу сделать это... я могу использовать свой... хм... мозг!
Изображение предоставлено http://longstreet.typepad.com/

Скачиваем SimpleTest от сюда, распаковываем в папку на ваш выбор -- лучше в папку с вашим кодом, или вашу PHP include_path для быстрого доступа.

Для этой статьи я настроил папки следующим образом:

Index.php запустит guestbook.php и выполнит метод view, чтобы отобразить записи. Внутри папки с классами мы сохраняем наш класс guestbook.php, а в папке test находится наша библиотека simpletest.


Шаг 2. Планируем свою атаку

Второй шаг является самым важным, здесь мы создаем наши тесты. Для этого вам вначале нужно обдумать то, что ваши функции будут делать, какие возможны входные данные, и какие будут на них ответы. Этот шаг напоминает игру Шахматы — вам нужно знать все о вашем сопернике (программе), включая все его слабые (возможные ошибки) и сильные стороны (что произойдет, если тесты успешно отработают).

Нарисуем схемы для нашего приложения гостевой книги:

Отображение

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

Шаг 3. Пишем тест!

Теперь мы можем написать наш первый тест. Начнем с создания файла guestbook_test.php внутри папки test.

Затем переведем то, что мы выделили на втором шаге.

Утверждения помогают вам убедиться в том, код возвращает то, что ожидается от него. Например, если от функции ожидается вернуть true в случае ее успешного завершения, то в нашем тесте нам следует проверить, что возвращаемое значение равно true.

Как можете видеть здесь, мы проверяем просмотр гостевой книги с записями и без. На втором шаге мы проверяем, что оба эти сценария выполняются. Вы так же наверно заметили, что каждый метод теста начинается со слова 'test'. Мы делаем так, потому что когда SimpleTest будет выполнять этот класс, то он будет искать все методы, которые начинаются со слова 'test' и выполнит их.

В нем тестовом классе мы также используем несколько методов для проверки утверждений, таких как assertTrue, assertIsA и assertEquals. Функция assertTrue проверяет является ли значение переменной равным true. AssertA проверяет относится ли переменная к определенному типу или классу. И наконец assertEquals проверяет, что переменная равна определенному значению.

В SimpleTest есть также и другие методы для проверки утверждений:

assertTrue($x) Неудача если $x имеет значение false
assertFalse($x) Неудача если $x равно true
assertNull($x) Неудача если $x имеет значение
assertNotNull($x) Неудача если $x не имеет значения
assertIsA($x, $t) Неудача если $x не является классом или типом $t
assertNotA($x, $t) Неудача если $x является классом или типом $t
assertEqual($x, $y) Неудача если выражение $x == $y равно false
assertNotEqual($x, $y) Неудача если $x == $y истинно
assertWithinMargin($x, $y, $m) Неудача если abs($x - $y) < $m ложно
assertOutsideMargin($x, $y, $m) Неудача если abs($x - $y) < $m истинно
assertIdentical($x, $y) Неудача если $x == $y ложно или разных типов
assertNotIdentical($x, $y) Неудача если  $x == $y истинно или типы совпадают
assertReference($x, $y) Неудача если $x и $y разные переменные
assertClone($x, $y) Неудача если $x и $y не являются идентичными копиями
assertPattern($p, $x) Неудача если регулярное выражение $p не подходит для $x
assertNoPattern($p, $x) Неудача если регулярное выражение $p подходит для $x
expectError($x) Проверяют любую предстоящую ошибку на совпадение
assert($e) Неудача если не было объекта $e

Список методов утверждений любезно предоставлен http://www.simpletest.org/en/unit_test_documentation.html


Шаг 4. Проиграть чтобы выиграть

Как только вы закончили писать свой код, следует запустить тесты. При первом запуске они обязательно завершатся неудачей. Если это не так, то скорее всего ваши тесты в действительности ничего не тестируют.

Чтобы запустить тесты, просто откройте файл guestbook_test.php в своем браузере. Сначала вы увидите это:

Это произошло, потому что мы до сих пор еще не создали класс guestbook. Чтобы сделать это, создает файл guestbook.php внутри директории classes. Этот класс будет содержать методы, которые мы собираемся использовать, но пока он еще ничего не содержит. Помните, сначала мы пишем тесты, а затем код, который тестируем.

Если вы снова запустите тесты, то результат будет выглядеть так:

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


Шаг 5. Решаем тесты путем написания кода


В каком то смысле, мы выглядим так, когда пишем код.
Изображение предоставлено http://fermentation.typepad.com/fermentation

Теперь когда у нас есть рабочие автоматизированные тесты, мы можем начать писать код. Откроем класс guestbook.php и начнем с решения нашего теста.

Этот класс guestbook.php содержит некоторые баги, которые мы сможем найти, когда наши тесты упадут.

Когда запустим тесты, то увидим следующее:

Вывод в тестах показывает нам в каком тесте и в каком конкретно утверждении наш код падает. От сюда, мы легко можем определить, что утверждения на 16 и 17 строках завершились неудачей.

Это ясно показывает, что возвращенный массив не содержит корректных ключей. Отталкиваясь от этого, мы теперь легко можем определить, какая часть нашего кода работает неверно,

Теперь снова запустим наши тесты:


Шаг 6. Рефакторинг


Изображения любезно предоставлены http://www.osborneink.com и http://phuketnews.phuketindex.com

Так как код, который мы здесь тестируем, достаточно простой, наши фиксы багов прошли достаточно быстро. Но если бы это было более сложное приложение, вам придется сделать несколько изменений в вашем коде, чтобы его было легче поддерживать. Проблема здесь в том, что изменения чаще всего подразумевают дополнительные баги. Вот где нам помогают автоматизированные тесты - как только мы вносим изменения, просто снова запускаем тесты. Если они снова проходят успешно, то это означает, что мы ничего не сломали. Если завершаются неудачей, то мы где-то допустили ошибку. Они так же сразу сообщают нам, где у нас ошибка, и скорее всего как ее можно исправить.


Шаг 7. Повторить по кругу

В конце концов когда ваша программа потребует новой функциональности, необходимо написать новые тесты. Это легко! Повторяем процедуры из второго шага (так как SimpleTest уже настроен и установлен), и начинаем круг сначала.


Заключение

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

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

Каковы ваши мысли на счет на разработки через тестирование? Вам это интересно или же наоборот вы считаете это простой потерей времени? Пишите в комментариях!

Advertisement
Did you find this post useful?
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.