Урок 4. Модульное тестирование на PHP. Тестирование вывода данных на экран


Содержание материала:

12 лучших фреймворков автоматизированного тестирования PHP

Основное различие между разработкой через тесты и разработкой через функционирование заключается в синтаксисе тестового кода: TDD использует соответствующий PHP код в файлах тестирования, в то время как BDD использует понятные человеку выражения, которые описывают поведение каждого элемента.

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

1. Selenium

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

2. PHPUnit

PHPUnit — это ориентированный на программистов фреймворк тестирования для PHP . Он построен на архитектуре XUnit для фреймворков модульного тестирования. PHPUnit можно управлять через командную строку, он предоставляет в наше распоряжение класс TestCase , который может быть расширен в соответствии с нашими потребностями.

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

3. Behat

Behat — фреймворк для тестирования PHP через функционирование ( BDD ), который позволяет писать понятный человеку код, описывающий, как должно функционировать приложение.

4. Kahlan

Kahlan — это полнофункциональный фреймворк модульного и BDD тестирования похожий на RSpec и JSpec , который использует описание синтаксиса и выводит тестирование PHP на новый уровень. Kahlan позволяет разобрать или проверить код напрямую ( как в Ruby или JavaScript ) без необходимости использования PECL -расширения. Он содержит большое количество функций, которые обеспечивают расширяемый и настраиваемый процесс тестирования.

5. Atoum

Atoum — это современный фреймворк для тестирования PHP , который позволяет запускать проверку отдельных модулей. Фреймворк ориентирован на использование новых возможностей, которые были введены в PHP 5.3 ( он не поддерживает более старые версии языка ).

6. Codeception

Codeception позволяет писать функциональные тесты, а также тесты подтверждения. Они тестируют РНР -приложения в целом, а не каждую его функцию по отдельности. Codeception позволяет подключить и настроить различные модули в соответствии с требованиями разработки. Он поддерживается многими фреймворками разработки PHP , такими как Symfony2 , Laravel4 , Yii , Phalcon и Zend Framework .

7. SimpleTest

SimpleTest — простой в использовании фреймворк TDD модульного тестирования PHP . SimpleTest поддерживает SSL , формы, прокси, фреймы и аутентификацию в базе данных, что позволяет быстро проверить общие задачи PHP .

8. Storyplayer

Storyplayer — это фреймворк тестирования полного стека, который позволяет осуществлять тестирование всей платформы. Команда Storyplayer предлагает специальный вид поддержки — создание сред тестирования по запросу. Это соответствует принципам TDD и позволяет писать функциональные тесты, с помощью которых можно проверить все приложение.

9. Peridot

Peridot — расширяемый фреймворк для тестирования PHP. Он имеет ориентированную на события архитектуру, что позволяет разработчикам легко настраивать фреймворк через плагины и отчеты. Peridot использует описание синтаксиса для создания тестов, которые четко описывают, как должен себя вести код приложения.

10. PHO

PHO — это фреймворк BDD -тестирования для PHP , во многом похожий на Jasmine и RSpec . Он характеризуется традиционным синтаксисом. Отслеживает команды для повторного запуска спецификаций в процессе разработки. Он также может быть расширен пользовательскими вычислениями и отчетами.

11. Mockery

Mockery — это простой, но гибкий фреймворк псевдо-объектов PHP , предназначенный для использования в модульном тестировании с помощью PHPUnit и PHP Spec . Он обеспечивает дублированное тестирование с API , способным четко определить все возможные операции и взаимодействия объектов, используя читаемый Domain Specific Language (DSL) .

12. PHP Specs

PHPSpec также « исповедует » BDD подход тестирования, но другой его подтип, который называется SpecBDD . В PHPSpec сначала нужно создать спецификации, которые описывают, как код приложения будет себя вести.

Данная публикация представляет собой перевод статьи « 12 Best PHP Automated Test Frameworks » , подготовленной дружной командой проекта Интернет-технологии.ру

Модульное тестирование в PHP средствами PHPUnit

Всем доброго времени суток! Сегодня я хотел бы поговорить с Вами о том, что такое модульное тестирование в PHP.

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

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

Однако давайте представим такую ситуацию: Вы пишете сайты для заказчиков на собственной системе управления контентом. Заказчики довольны, Вы чувствуете себя прекрасно, но в один день понимаете, что система, которую Вы разработали, уже не отвечает предъявляемым к ней требованиям, она нуждается в изменениях. И Вы начинаете переписывать одну часть системы за другой.

Наступает такой момент, когда один новый класс, метод, условие или цикл — рушат всю систему. Изменения в одном месте приводят к ошибкам в другом. И вот они уже лезут без конца, как из рога изобилия, и становиться понятно, что так дальше невозможно. А все было бы гораздо лучше, если бы сначала, помимо всего прочего, были бы написаны PHP Unit тесты. Не зря ведь говорит, Мартин Фаулер, что когда бы Вы ни пытались напечатать что-то через print в целях отладки или рефакторинга, лучше напишите это в виде Unit теста.

Итак, с теорией вроде ознакомились, теперь перейдем непосредственно к коду. Здесь необходимо сделать важные замечания, все операции проводятся на ПК под управлением Windows 7 c установленным PHP 7 версии. Дальше будет по пунктам.

1) Скачиваем по ссылке файл phpunit-6.3.0.phar, который представляет собой исполняемый PHP архив.

2) Загруженный файл перемещаем в папкуC:\bin. В этой же папке создаем файл phpunit.bat, записываем в него следующее содержимое: @php C:\bin\phpunit-6.3.0.phar %*

Учтите, что путь C:\bin должен быть определен в системной переменной PATH, иначе при попытке выполнить в консоли команду phpunit Вы получите ошибку!

3) Откройте консоль и выполните команду phpunit, и если все правильно, то в консоли должна отобразиться справка.

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

array_push($stack, ‘foo’); // добавили одним элемент в массив
$this->assertEquals(‘foo’, $stack[count($stack)-1]); // проверили на равенство
$this->assertEquals(1, count($stack));

$this->assertEquals(‘foo’, array_pop($stack));
$this->assertEquals(0, count($stack));
>
>
?>

Код хорошо комментирован, однако поясню пару моментов. Краеугольным камнем Unit тестирования является утверждение (assertion). Утверждение — это Ваше предположение об ожидаемом значении, или другими словами Вы утверждаете, что значение переменной, элемента массива, результат выполнения метода и т.д. будет равно такому-то значению. В примере выше, при первоначальном создании массива, ожидаемое значение его длины — 0. Так оно и есть на самом деле в нашем примере.

В данном случае мы используем только одно утверждение assertEquals, хотя в классе TestCase библиотеки PHPUnit их несколько десятков, на все случаи жизни, так сказать.

Так тест мы написали, а что дальше? А дальше его надо запустить. Для этого открываем консоль, переходим в папку с нашим тестом (PHP Unit тесты обычно располагаются в отдельной папке tests) и запускаем команду phpunit, передав ей в аргументе текущий каталог (обозначается одной точкой).

cd C:/Projects/php/tests && phpunit .

Данная команда автоматически пройдется по всем PHP тестам, которые есть в данном каталоге. По завершении выполнения, она выведет информацию о том, сколько тестов пройдено и, возможно, провалено.

Таким образом, сегодня мы с Вами выяснили что такое Unit тестирование в PHP, что применять его не только полезно, но и нужно. А если Вы знаете PHP плохо, или не знаете его совсем, то специально для Вас у меня есть отличный видеокурс «PHP и MySQL с Нуля до Гуру 2.0», в котором, я, в частности, подробно разбираю тему модульного (Unit) тестирования в PHP.

Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)!

Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/myrusakov.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk.com/rusakovmy.

Если Вы не хотите пропустить новые материалы на сайте,
то Вы можете подписаться на обновления: Подписаться на обновления

Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.

Порекомендуйте эту статью друзьям:

Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):

Она выглядит вот так:

  • BB-код ссылки для форумов (например, можете поставить её в подписи):
  • Комментарии ( 2 ):

    А нет статьи где описывается как сделать регистрацию\вход\управление пользователями и т.д. всё вместе

    Здравствуйте! На сайте есть статьи по указанным Вами темам, их можно найти через форму поиска. Однако, если Вы хотите получить отличные знания по этим темам, то можете посмотреть курс, в котором эти темы разбираются очень подробно. Вот ссылка: https://srs.myrusakov.ru/php2?utm_source=MyRusakov.ru&utm_campaign=php2&utm_content=article_1017

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

    Copyright © 2010-2020 Русаков Михаил Юрьевич. Все права защищены.

    Тест с помощью пхп, проверка ответов с бд

    06.02.2012, 19:22

    Тест на PHP с поочередным выводом заданий и ответов
    Друзья, доброго времени суток! Возникла такая задача: надо реализовать на сайте нечто похожее на.

    Как заблокировать ип адресс с помощью пхп?
    У меня такой вопрос: как заблокировать ип какой-то машины, ну например злостного хакера.

    Варианты ответов (тест)
    Если derived class (Class2) содержит переопределеную функцию из the base class (Class1), как может.

    Случайное размещение ответов на тест
    Здравствуйте! Есть программа — тест. Есть документ, где вопросы, ответы и верный вариант ответа.

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

    07.02.2012, 12:04 2

    Для начала нужно создать БД mysql, в ней таблицу «answers» с такими полями:
    id (ID вопроса в БД, он будет подставляться при выводе ответов к вопросу)
    for_id (ID ответа, для которого предназначен данный ответ)
    answer (Текст ответа, например «Ответ №1»)
    believed (Этот столбец нужен для проверки, верен ли ответ. В него ставим при INSERT’e единицу если ответ верен)

    Как написать скрипт для проведения собственного тестирования на PHP?

    В этой статье речь пойдет о реализации простого тестирования при помощи PHP .

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

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

    Сам тест будет хранить у нас в index.html , как форма со всеми её элементами. А проверка тестирования будет проходить в файле test.php .

    Подготовим тест и напишем разметку нашей страницы:

    Здесь стоит отметить то, что в атрибутах name лучше задавать понятные названия, с которыми вам потом придется оперировать в PHP файле. Так же мы советуем вам в атрибутах value так же задавать понятные значения, чтобы не приходилось оперировать непонятными цифрами(именно это значения сохраняется и отправляется в обработчик).

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

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

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

    Проверить работоспособность примера можно тут:

    ez code

    Просто о сложном.

    Основы Unit тестирования в PHP с помощью PHPUnit

    Знакомая ситуация: вы разрабатываете приложение, решаете проблемы и, иногда, возникает ощущение, что вы ходите по кругу. Правите один баг и сразу появляется другой. Иногда это тот, который вы поправили 30 минут назад, иногда — новый. Отладка становится очень сложной, но есть хороший и простой выход из этой ситуации. Юнит тесты могут не только уменьшить боль при разработке, но и помогут писать код, который легче сопровождать и легче изменять.

    Для понимания того, что такое модульное тестирование, необходимо определить понятие «модуля». Модуль (или unit) — это часть функционала приложения результат работы которой мы можем проверить (или протестировать). Модульное тестирование — это, собственно проверка, что данный модуль работает именно так как ожидается.Написав один раз тесты, всякий раз когда вы внесете изменения в код, вам останется только запустить тесты, для проверки, что всё правильно. Таким образом вы всегда будете уверены, что своими изменениями вы не сломаете систему.

    Мифы о юнит тестировании

    Не смотря на всю пользу юнит тестирования, не все разработчики им пользуются. Почему? Есть несколько ответов на этот вопрос, но все они — не слишком хорошие оправдания. Рассмотрим распространенные причины и попытаемся разобраться почему они не оправданы.

    Написание тестов занимает слишком много времени

    Самая распространенная причина: написание тестов — это долго. Конечно, некоторые среды разработки сгенерируют для вас набор простейших тестов, но написание качественных, нетривиальных тестов для вашего кода занимает время. Это нормальная практика — уделить некоторое врмя написанию юнит тестов, в результате это поможет сэкономить гораздо больше времени при сопровождении проекта. Если вы разрабатывали сайт, то наверняка, при добавлении нового функционала, вы тестировали его просто кликая по всевозможным ссылкам на сайте. Простой запуск набора тестов будет гораздо быстрее, нежели ручная проверка всех функций.

    Не надо тестов — код и так работает!

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

    Это неинтересно

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

    Пример

    Приступим к практике. В наших примерах мы будем использовать библиотеку PHPUnit. Самый простой способ установить PHPUnit — затянуть с PEAR канала.

    Если все пройдет хорошо, то все необходимые инструменты будут установлены. Если вы хотите установить PHPUnit вручную — инструкцию вы найдете здесь.

    Первый тест

    Используя PHPUnit, вы будете писать тестовые классы, содержащие тестовые методы и всё это должно удовлетворять следующим соглашениям:

    • В большинстве случаев вы будете наследовать класс PHPUnit_Framework_TestCase , что предоставит вам доступ к встроенным методам, например, setUp() и tearDown() .
    • Имя тестирующего класса образуется добавлением слова Test к имени тестируемого класса. Например, вы тестируете класс RemoteConnect , значит имя тестирующего — RemoteConnectTest .
    • Имена тестирующих методов всегда должны начинаться с “test” (например, testDoesLikeWaffles() ). Методы должны быть публичными. Вы можете использовать приватные методы в своих тестах, но они не будут запускаться как тесты через PHPUnit.
    • Тестирующие методы не принимают параметров. Вы должны писать тестирующие методы максимально независимыми и самодостаточными. иногда это неудобно, но вы получите более чистые и эффективные тесты.

    Напишем небольшой класс для тестирования RemoteConnect.php :

    Если мы хотим протестировать функционал для соединения с удаленным сервером, то мы должны написать подобный тест:

    Тестирующий класс наследует базовый PHPUnit класс, а значит и всю необходимую функциональность. Первые два метода — setUp и tearDown — пример этой встроенной функциональности. Это вспомогательные функции, которые являются частью каждого теста. Они выполняются до запуска всех тестов и после соответственно. Но сейчас нас интересует метод testConnectionIsValid . Этот метод создает объект типа RemoteConnect , и вызывает метод connectToServer .

    Мы вызываем еще одну вспомогательную функцию assertTrue в нашем тесте. Эта функция определяет простейшее утверждение (assertion): она проверяет является ли переданное значение истиной. Другие вспомогательные функции выполняют проверки свойств объектов, существования файлов, наличия ключей в массиве, или соответствия регулярному выражению. В нашем случае мы хотим убедиться в правильности подключения к удаленному серверу, т.е. в том, что функция connectToServer возвращает true .

    Запуск тестов

    Запускаются тесты простым вызовом команды phpunit с указанием вашего php файла с тестами:

    PHPUnit запускает все тесты и собирает немного статистики: успешно завершился тест или нет, количество тестов и утверждений (assertions) и выводит все это. Пример вывода:

    Для каждого выполненного теста будет выведен результат: «.» если тест завершился успешно, “F” если тест не пройден, “I” если тест невозможно завершить и “S” если тест был пропущен.

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

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

    Топ-пост этого месяца:  Pr.Sape — как покупать вечные ссылки и размещать статьи, а так же как добавить свой сайт в биржу Пр
    AssertTrue / AssertFalse Проверка переданных значений на равенство true/false
    AssertEquals Проверка переданных значений на равенство
    AssertGreaterThan Сравнивает две переменные (есть так же LessThan, GreaterThanOrEqual, and LessThanOrEqual)
    AssertContains Содержит ли переданная переменная заданное значение
    AssertType Проверка типа переменной
    AssertNull Проверка на равенство null
    AssertFileExists Проверка существования файла
    AssertRegExp Провка по регулярному выражению

    Например есть функция, которая возвращает объект ( returnSampleObject ) и мы хотим убедиться в том, что возвращаемый объект будет нужного нам типа:

    Один тест — одно утверждение (assert)

    Как и во всех областях разработки программного обеспечения, в тестировании есть лучшие практики. Одна из них — «один тест — одно утверждение» (one test, one assertion). Это правило поможет писать небольшие и легко читаемые тесты. Но иногда возникают мысли: «Раз уж мы здесь проверяем это, то и кое-что другое заодно проверим!». Например:

    Наш тест testIsMyString проводит два разных теста. Сначала тест на пустую строку (длина должна быть > 0), затем тест на содержание в строке подстроки «42». Но этот тест может провалиться как в первом так и во втором случае, а сообщение об ошибке в обоих случаях будет одинаковым. Поэтому стоит придерживаться принципа «один тест — одно утверждение».

    Test-driven Development (разработка через тестирование)

    Было бы нехорошо, говоря о тестировании не упомянуть о распространенной технике разработки — разработке через тестирование (test driven development). TDD — это техника, используемая при разработке программного обеспечения. Основная идея этой техники заключается в том, что сначала пишутся тесты и только после написания тестов пишется код приложения, который пройдет эти тесты.

    Как создать онлайн тест на php

    Иногда людям не знающим программирования, нужно создать онлайн тест на php для своего сайта, на самом деле все очень просто, постараюсь наглядно объяснить.

    Для решения данной задачи мы будем использовать простую HTML форму и POST запросы. И так приступим.

    1. Нам потребуется создать файл php, в общем по сути не важно, если вы используете CMS, то пишете там, где можно вставить php код.

    2. Создадим простую форму с полями «radio», то есть для выбора вариантов ответа и кнопкой для результатов.

    В результате получим:

    3. Отлично мы создали форму. Теперь в неё нужно внести изменения, чтобы она работала.

    Добавим атрибуты к форме.

    method=»POST» — метод отправки php форм.

    action=»result.php» — файл, в который будут отправляться результаты формы.

    4. Присвоим уникальное имя вариантам ответов, чтобы объединить их между собой, для одного вопроса — name = «имя»:

    5. А теперь пропишем имя ответов к каждому варианту, например ‘value = «a»‘

    В результате получим:

    6. Создадим файл result.php в том же каталоге, в котором создадим скрипт теста на php для обработки и вывода результата правильных и не правильных ответов.

    7. Перед html кодом добавим скрипт php:

    Описание: Здесь все довольно просто, мы создаем простое условие для каждого вопроса. Если в вопросе [q1] вариант «a», то прибавляем к переменной «$ot» + 1 ($ot — количество правильных ответов), если другой вариант то записываем в переменную $not «неправильных ответов» + 1.

    8. Теперь выводим на страницу в html коде правильные и неправильные ответы:

    Заключение. Теперь у вас есть простой тест на php и html, теперь можно и запустить его для онлайн. Вы можете добавить сколько угодно вариантов ответов по аналогии. Добавить стилей и улучшить код. Можно было все сделать намного грамотней, но проще этого не придумаешь. Работа у теста такая: выбрали варианты, нажали на кнопку «результат», нас перекидывает на вторую страницу, где выводится подсчет правильных и неправильных ответов. Удачи!

    С помощью POST-запроса можно реализовать обратную форму, о том, как это делается читайте в статье — Форма обратной связи php без спама.

    Урок 4. Модульное тестирование на PHP. Тестирование вывода данных на экран

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

    Содержание

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

    Поставщики данных (data providers)

    Возьмем пример сложнее (см. полную версию в [src/Strings.php] в архиве с исходниками). Приведенный ниже метод возвращает фразу в правильном склонении в зависимости от числа, идущего с текстом:

    Тестовый метод (см. [tests/unit/StringsTest.php] ):

    Data prov >@dataProvider . Требования в методу-поставщику: он должен возвращать двумерный массив значений. Каждый подмассив — это набор данных на один тест-случай. Подмассив должен содержать элементы в том же количестве и порядке, как как это требуется в параметрах тест-метода.

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

    Если в вашем поставщике данных возникнет исключение, тогда PhpUnit закончит тестирование с сообщением «No tests executed!», что вообще ни о чем не говорит. Включайте отладчик и ищите, что у вас не так в DataProvider .

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

    Прим.: в том же тестовом классе есть пример, как бы выглядел тест без использования DataProvider.

    Еще один пример организации DataProv >[tests/dummy_examples/ProductValidatorTest.php] , метод test_checkAttrValueWithType() .

    Важно: во время выполнения теста сначала вызывается поставщик данных, потом setUpBeforeClass() и setUp() (о них в следующем разделе). Т.е. в dataProvider() нельзя полагаться на данные, которые могут быть заданы в setUpBeforeClass() | setup() тестового метода.

    Фикстуры

    Одной и наиболее времязатратных частей написания тестов является установка «мира» приложения в известное состоянии и откат к этому состоянию после теста. Это известное состояние называется фикстурой теста. (перевод из мануала PHPUnit).

    PHPUnit позволяет организовывать тестовое окружение в отдельно взятом классе, а так же для каждого выполняемого теста в классе. Для этого есть группа методов, о которых подробно расписано в мануале PHPUnit: 4. Fixtures. Кратко расскажу.

    Приведенные ниже методы (если они вам нужны для тестов) нужно реализовывать прямо в ваших тестовых классах:

    • сначала вызов всех методов поставщиков данных
    • setUpBeforeClass() статичный метод, выполняется на этапе создания тест-класса. Аналогичный ему статический метод tearDownAfterClass() выполняется после всех тестов
    • Динамический метод setUp() выполняется перед каждым тестом, tearDown() после каждого теста
    • Динамический метод assertPreConditions() выполняется перед первым утверждением в каждом тесте. assertPostConditions() выполняется, если тест успешно завершился.

    Прим: найдите в мануале пример Example 4.2 и результат его выполнения. Этот пример показывает последовательность вызова всех методов фреймворка для установки фикстур.

    Как использовать эти методы — решать вам. Идея простая: все, что можно инициализировать для всех/каждого тест-метода — выносят в методы setUpBeforeClass() и setUp() соответственно. Если после теста требуется уборка (например, очистка кеша), вызываются соответствующие методы отката.

    Примеры использования в архиве исходников можно посмотреть в [tests/unit/FSTest.php] и [tests/dummy_examples/ProductValidatorTest.php] . Содержимое этих методов может быть пока непонятно, но можно уловить мысль, что в них писать. Коротко: что-угодно общее для всех тест-методов.

    Еще такой интересный момент: в unit-тестах можно принебречь оптимизацией или производительностью в пользу контролируемого поведения. Поэтому часто, вместо однократной инициализации окружения для всего класса через метод setUpBeforeClass() используют setUp() для создания известной среды для каждого тест-метода.

    Подмена зависимостей

    Подмена средствами PHPUnit

    Тут мне трудно было выбрать, что рассказывать в статье, а за чем отправить в мануал PHPUnit: 9. Test Doubles. Там всего одна страница, и на примерах изложено вполне доступно. Я не хочу переписывать эту часть из документациии. Но все же скажу пару слов.

    Как делается подмена зависимостей в PHPUnit 6.1. Напомню пример из теории (прим.: там класс назывался SomeClass ):

    Тест с подменой зависимостей:

    Как видно из кода выше, для подделки использовали разные методы. Дело в том, что метод createMock() — это обертка. На самом деле, в нем выполняется цепочка методов PHPUnit:

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

    Подделки могут возвращать не только скалярные значения. У PHPUnit есть методы на все возможные случаи подмены.

    Еще один пример подделки зависимости [tests/dummy_examples/ProductValidatorTest.php] , метод test_validateAttributes() , блок кода Act.

    В тест-методах можно строить утверждения относительно подмененного объекта (понятие mock помните?). И вот тут еще большее поле для деятельности. У меня нет простого, но сколь-нибудь полезного примера, поэтому лучше смотрите примеры в мануале PHPUnit. Mock Objects, если у вас возникнет такая необходимость. Как было отмечено в теоретической части статьи, проверка взаимодействий — это самый крайний случай в модульном тестировании, когда иначе никак проверить метод не получается.

    PHPUnit позволяет проверить, что подменяемый метод вызван ожидаемое количество раз, с определенными аргументами или их последовательностью (если вызовов несколько). Так же можно описать условия, при которых ожидаются определенные аргументы. Я сильно в эту тему не вдавался, обычно хватает тестирования в первых двух направлениях (результат или состояние).

    Подмена с использованием Mockery

    Mockery это PHP-фреймворк, запиленный специально для подмены объектов в unit-тестах. Он разработан, как альтернатива библиотеке подмены в PHPUnit, может использоваться в нем или как отдельный модуль, т.е. его можно подключить в другие тестовые движки. Основная фича — использование человекопонятного предметно-ориентированного языка (Domain-specific language, DSL).

    Простым примером предметно-ориентированного языка является SQL для СУБД.

    Приведенный выше тест (см. Подмена средствами PHPUnit) станет таким:

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

    AspectMock

    AspectMock проксирует все вызовы всех методов и позволяет их налету подменить. Я попробовал — это действительно круто. Но в итоге с моим движком он работать не смог, конфликтнул где-то. Т.о. возьмите на вооружение, если сможете подружить его с вашим проектом.

    Настройка. В bootstrap.php тестов прописываем типа этого (в исходниках см. [tests/bootstrap.php] ):

    Пример все того же теста, но теперь с подменой через AspectMock:

    «Killer feature» AspectMock именно в возможности подменить вообще любой класс или метод, независимо от того, был ли он как-либо внедрен или явно вызывается прямо в функции. Может оказаться полезно, если код изначально не был создан с оглядкой на тесты и представляет собой залипуху и кучки байт.

    Тестирование с виртуальной файловой системой

    Проблема: есть метод, взаимодействующий с реальной файловой системой. Нужно протестировать именно само взаимодействие, но при этом подменить ФС на виртуальную.

    Ставим vfsStream и пользуемся 🙂

    Эта библиотека эмулирует любые операции с файловой системой: CRUD файлов и каталогов, права доступа и т.д. Подробное использование расписано в мануале. Простой пример использования:

    Более расширенный пример с кучей вариантов использования виртуальной ФС см. в архиве исходников [/tests/unit/FSTest.php]

    • vfsStream вообще не любит слеши. Например, создание каталога с концевым слешем или без него — это два разных набора в vfsStream::getChildren() . За их использованием нужно внимательно следить во избежание проблем в тестовых утверждениях.
    • с виртуальной ФС не работает функция PHP glob() . Описание проблемы и вариант решения тут. Коротко: нужно придумать свою функцию, выполняющую ту же работу, что и glob() и использовать ее во всех местах, где возможно обращение к виртуальной ФС. Мое решение основано на PHP::DirectoryIterator , его можно увидеть в FS::dirList() .

    Тестирование исключений

    В PHPUnit метод expectException() , а так же директива @expectedException используются в тестах для указания ядру фреймворка «ожидать такое-то исключение». В итоге тест считается пройденным если исключение возникло.

    И тут есть ньюансик: после того, как PHPUnit поймает ожидаемое исключение, выполнение тест-метода прекратится! Т.е. expectException() — это аналог assert-метода, только с прерыванием. Есть так же методы на проверку кода и сообщения исключения.

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

    1. забить на проверку исключений в принципе;
    2. писать тест-методы на нормальное поведение и на каждое пробрасываемое исключение отдельно;
    3. в тест-методе оформлять блоки try. catch ;
    4. использовать data provider.

    Последний вариант мне представляется наиболее предпочтительным. Причем data provider позволяет описать вообще все тест-кейсы в одном методе.

    См. скрипт архива исходников [tests/dummy_examples/ExceptionsTest.php] . Пример ExceptionsTest::test_normalizePriority() демонстрирует решение с data prov >test_getTargetFileName() для демонстрации исключения в одном методе вместе с нормальными ситуациями.

    Пример отдельного теста только на проброс исключения см. в [unit_simple/tests/FSTest.php] метод test_fuse_removeDir() . Исключение ожидается всего одно, data provider там не нужен. Но чтобы создать исключительную ситуацию в этом методе, требуется большая подготовка, поэтому тест-метод оформлен отдельно.

    Тестирование запросов в базу данных

    Тестирование взаимодействия с БД — это скорее интеграционный тест, но все же стоит один раз напрячься и сделать. Это несложно. Хотя зависит от проекта, я не настаиваю 🙂

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

    В общих чертах: вам нужна будет еще одна база данных помимо боевой, структура должна соответствовать рабочей БД. Если используете миграции, поддержка актуальной структуры — не проблема. В PHPUnit нужно добавить модуль:

    Далее см. в архиве исходников [tests/dummy_examples/DBEmptyTest.php] .

    В тестовый класс нужно подключить трейт TestCaseTrait (до версии PHPUnit 6.1 был суперкласс для наследования, теперь трейт) и реализовать два абстрактных метода: подключение к базе и загрузка данных (фикстур) в таблицы.

    Прим: реализация подключения к базе зависит от конкретного приложения. Методы getConnection() и getDataSet() выполняются перед каждым тест-методом.

    Как это работает: PHPUnit подключается к базе, очищает таблицы и грузит в них данные, которые вы укажете. Ваш проверяемый класс должен уметь подключаться к тестовой БД. Далее обычная процедура тестирования — Arrange Act Assert. В конце — откат через tearDown() , если требуется.

    PHPUnit предоставляет кучу форматов для загрузки данных: несколько XML форматов, YAML, CSV, arrays и какие-то велосипеды. Имхо, удобнее всего YAML.

    См. в архиве исходников пример подготовленных данных — [tests/dummy_examples/fixtures/*.yml] , тест в [tests/dummy_examples/LabelModelTest.php]

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

    Тестирование модулей

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

    Тестирование модулей (или блоков) представляет собой процесс тестирования отдельных подпрограмм или процедур программы. Здесь подразумевается, что, прежде чем начинать тестирование программы в целом, следует протестировать отдельные небольшие модули, образующие эту программу. Такой подход мотивируется тремя причинами. Во–первых, появляется возможность управлять комбинаторикой тестирования, поскольку первоначально внимание концентрируется на небольших модулях программы. Во–вторых, облегчается задача отладки программы, т.е. обнаружение места ошибки и исправление текста программы. В–третьих, допускается параллелизм, что позволяет одновременно тестировать несколько модулей.

    Цель тестирования модулей – сравнение функций, реализуемых модулем, со спецификациями его функций или интерфейса.

    Тестирование модулей в основном ориентировано на принцип «белого ящика». Это объясняется, прежде всего, тем, что принцип «белого ящика» труднее реализовать при переходе в последующем к тестированию более крупных единиц, например программ в целом. Кроме того, последующие этапы тестирования ориентированы на обнаружение ошибок различного типа, т. е. ошибок, не обязательно связанных с логикой программы, а возникающих, например, из–за несоответствия программы требованиям пользователя.

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

    Метод сандвича.Тестирование методом сандвича представляет собой компромисс между восходящим и нисходящим подходами. Здесь делается попытка воспользоваться достоинствами обоих методов, избежав их недостатков. При использовании этого метода одновременно начинают восходящее и нисходящее тестирование, собирая программу снизу и сверху, встречаясь где– то в середине. Точка встречи зависит от конкретной тестируемой программы и должна быть заранее определена при изучении ее структуры. Например, если разработчик может представить свою систему в виде уровня прикладных модулей, затем уровня модулей обработки запросов, затем уровня примитивных функций, то он может решить применять нисходящий метод на уровне прикладных модулей (программируя заглушки вместо модулей обработки запросов), а на остальных уровнях применить восходящий метод. Метод сандвича сохраняет такое достоинство нисходящего и восходящего подходов, как начало интеграции системы на самом раннем этапе. Поскольку вершина программы вступает в строй рано, мы, как в нисходящем методе, уже на раннем этапе получаем работающий каркас программы. Так как нижние уровни программы создаются восходящим методом, снимаются проблемы нисходящего метода, которые были связаны с невозможностью тестировать некоторые условия в глубине программы.

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

    Восходящее тестирование.Программа собирается и тестируется «снизу вверх». Только модули самого нижнего уровня («терминальные» модули; модули, не вызывающие других модулей) тестируются изолированно, автономно. После того как тестирование этих модулей завершено, вызов их должен быть так же надежен, как вызов встроенной функции языка или оператор присваивания. Затем тестируются модули, непосредственно вызывающие уже проверенные. Эти модули более высокого уровня тестируются не автономно, а вместе с уже проверенными модулями более низкого уровня. Процесс повторяется до тех пор, пока не будет достигнута вершина. Здесь завершается и тестирование модулей, и тестирование сопряжений программы.

    При восходящем тестировании для каждого модуля необходим драйвер: нужно подавать тесты в соответствии с сопряжением тестируемого модуля. Одно из возможных решений – написать для каждого модуля небольшую ведущую программу. Тестовые данные представляются как «встроенные» непосредственно в эту программу переменные и структуры данных, и она многократно вызывает тестируемый модуль, с каждым вызовом передавая ему новые тестовые данные. Имеется и лучшее решение: воспользоваться программой тестирования модулей – это инструмент тестирования, позволяющий описывать тесты на специальном языке и избавляющий от необходимости писать драйверы.

    Топ-пост этого месяца:  Сравнение методов Javascript Array push и concat разница в производительности объединения массивов

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

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

    При этом подходе возникают два вопроса: 1. «Что делать, когда тестируемый модуль вызывает модуль более низкого уровня (которого в данный момент еще не существует)?» и 2. «Как подаются тестовые данные?»

    Ответ на первый вопрос состоит в том, что для имитации функций недостающих модулей программируются модули – «заглушки», которые моделируют функции отсутствующих модулей.

    Интересен и второй вопрос: в какой форме готовятся тестовые данные и как они передаются программе? Если бы головной модуль содержал все нужные операции ввода и вывода, ответ был бы прост: тесты пишутся в виде обычных для пользователей внешних данных и передаются программе через выделенные ей устройства ввода. Так, однако, случается редко. В хорошо спроектированной программе физические операции ввода– вывода выполняются на нижних уровнях структуры, поскольку физический ввод– вывод – абстракция довольно низкого уровня. Поэтому для того, чтобы решить проблему экономически эффективно, модули добавляются не в строго нисходящей последовательности (все модули одного горизонтального уровня, затем модули следующего уровня), а таким образом, чтобы обеспечить функционирование операций физического ввода– вывода как можно быстрее. Когда эта цель достигнута, нисходящее тестирование получает значительное преимущество: все дальнейшие тесты готовятся в той же форме, которая рассчитана на пользователя.

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

    Преимуществом нисходящего подхода очень часто считают отсутствие необходимости в драйверах; вместо драйверов вам просто следует написать «заглушки».

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

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

    Метод «большого скачка».Вероятно, самый распространенный подход к интеграции модулей – метод «большого скачка». В соответствии с этим методом каждый модуль тестируется автономно. По окончании тестирования модулей они интегрируются в систему все сразу. Метод «большого скачка» по сравнению с другими подходами имеет много недостатков и мало достоинств. Заглушки и драйверы необходимы для каждого модуля. Модули не интегрируются до самого последнего момента, а это означает, что в течение долгого времени серьезные ошибки в сопряжениях могут остаться необнаруженными. Если программа мала (как, например, программа загрузчика) и хорошо спроектирована, метод «большого скачка» может оказаться приемлемым. Однако для крупных программ метод «большого скачка» обычно губителен.

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

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

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

    Детального разбора обоих методов мы делать не будем, приведем лишь некоторые общие выводы.

    1.Монолитное тестирование требует больших затрат труда. При пошаговом же тестировании «снизу– вверх» затраты труда сокращаются.

    2.Расход машинного времени при монолитном тестировании меньше.

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

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

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

    6.Результаты пошагового тестирования более совершенны.

    В заключение отметим, что п. 1, 4, 5, 6 демонстрируют преимущества пошагового тестирования, а п. 2 и 3 – его недостатки. Поскольку для современного этапа развития вычислительной техники характерны тенденции к уменьшению стоимости аппаратуры и увеличению стоимости труда, последствия ошибок в математическом обеспечении весьма серьезны, а стоимость устранения ошибки тем меньше, чем раньше она обнаружена; преимущества, указанные в п. 1, 4, 5, 6, выступают на первый план. В то же время ущерб, наносимый недостатками (п. 2 и 3), невелик. Все это позволяет нам сделать вывод, что пошаговое тестирование является предпочтительным.

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

    Во– первых, термины «нисходящее тестирование», «нисходящая разработка», «нисходящее проектирование» часто используются как синонимы. Действительно, термины «нисходящее тестирование» и «нисходящая разработка» являются синонимами (в том смысле, что они подразумевают определенную стратегию при тестировании и создании текстов модулей), но нисходящее проектирование – это совершенно иной и независимый процесс. Программа, спроектированная нисходящим методом, может тестироваться и нисходящим, и восходящим методами.

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

    Не нашли то, что искали? Воспользуйтесь поиском:

    codedokode / Тестирование.md

    Если ты пишешь код, то наверняка его тестируешь. Если речь о какой-то функции, то ты можешь вызывать ее с разными аргументами, и смотреть, что она вернет. Если ты сверстал сайт, то ты открываешь его в браузере, жмешь ссылки и кнопки, проверяешь что все сделано верно. Это называется ручное тестирование — человек проверяет работу программы. Нельзя ли эту задачу переложить на плечи роботов? Обычно можно, и это называется автоматизированное тестирование.

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

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

    В больших компаниях может быть отдельная группа людей, которые занимаются только тестированием. Обычно их называют QA (Quality Assurance).

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

    Что можно тестировать

    Тестировать программу можно на разных уровнях: код (юнит-тесты и интеграционное тестирование), API (если оно есть) и GUI. Разные виды тестов лучше подходят в разных ситуациях.

    Код тестируется на разных уровнях:

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

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

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

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

    Юнит-тесты хорошо тестируют такой код, который содержит какую-то логику. Если в коде мало логики, а в основном содержатся обращения к другим классам, то юнит-тесты написать может быть сложно (так как надо сделать замену для других классов и не очень понятно, что именно проверять?).

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

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

    Ради ускорения выполнения тестов, обычно используют базу данных, храняющую данные в памяти, а не на диске (MySQL и sqlite умеют это).

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

    Тестировать можно не любой код. Если например в твоем коде жестко прописаны параметры соединения с базой данных или пути к папкам без возможности их поменять, ты вряд ли сможешь использовать для тестов временную БД. То же самое, если классы в твоем коде сильно связаны и ты не используешь dependency injection, если используются глобальные переменные или везде статические методы. В общем, пиши качественно.

    API (wiki) — это набор функций, которые можно вызывать, чтобы получить какие-то данные. Ну, например, у яндекс-карт есть АПИ геокодера. Отправив к нему запрос с географическим адресом, ты можешь получить координаты точки (и наоборот), а у Центробанка есть API, которое возвращает официальный курс валют в заданный день.

    Если у твоего приложения есть API, то можно тестировать его, посылая заранее подготовленные запросы и сравнивая пришедший ответ с ожидаемым.

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

    GUI тесты еще называют End-to-End (E2E) или приемочные (aceptance) тесты.

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

    Для тестирования веб-приложения (сайта) необходимо имитировать работу браузера. Для этого есть разные подходы. Есть простые инструменты, которые лишь умеют отправлять HTTP-запросы к серверу и анализировать полученный HTML-код, и более продвинутые, которые либо используют настоящий браузерный движок (вроде PhantomJS) в «headless» режиме (то есть без вывода окошка со страницей на экран), а самые продвинутые предоставляют драйверы, с помощью которых можно контролировать реальный браузер (Selenium).

    Простые HTML-браузеры хороши тем, что работают гораздо быстрее, но они не интерпретируют CSS- и JS-код и не могут проверить например видимость кнопки или работу скриптов. PhantomJS это умеет. Selenium позволяет получить наиболее полноценную имитацию действий пользователя, в том числе например тестирование в определенной версии браузера или использование флеш-плагина, но сложен в настройке: требуется где-то запускать эти браузеры, надо поднимать сеть виртуальных машин с нужными операционными системами и тесты на нем медленнее работают.

    PhantomJS и Selenium умеют делать скриншот страницы, который можно будет посмотреть при неудачном выполнении теста.

    Тестируем без фанатизма

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

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

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

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

    Также, есть подход, когда сначала пишутся тесты (которыми задаются требования к коду), а только потом сам код. Это называется TDD. Есть также его последователь BDD, где сценарии пишутся на странном языке Gherkin и напоминают обычный текст.

    Что важно помнить

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

    Тесты должны выполняться в контролируемом окружении.

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

    Надо тестировать и позитивные, и негативные сценарии. К примеру, при тестировании формы регистрации надо проверить не только как она работает при вводе правильных данных, но и как она работает с неправильными данными (должна выдавать сообщение об ошибке).

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

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

    Еще способы тестирования и повышения надежности кода

    Использование assertions и тайп-хинтов. assert() (мануал) — это функция, которая выдает ошибку если условие в скобках не выполняется. Например, если ты сделал функцию, и она принимает только числа от 0 до 5, логично в начале поставить assert для проверки этого:

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

    Топ-пост этого месяца:  Как сделать опрос в Фейсбуке создаём голосование, темы опросов

    Type hint — это конструкция, которая указывает что аргумент функции должен быть массивом или объектом определенного класса. При попытке передать в нее что-то другое произойдет фатальная ошибка. Используй тайп хинты везде, где можно. Пример:

    Тестирование через вызов контроллера — это что-то вроде GUI тестирования, только без отправки HTTP-запросов (и имитации работы браузера), используя вызов контроллеров напрямую. Обычно это используется с приложениями на фреймворках вроде Yii2 или Symfony2.

    Статический анализ кода. Специальная программа проверяет исходники, не запуская код, с целью найти опечатки и неправильные куски кода. Такая программа хорошо ищет ошибки ситаксиса: $x = ($x + 1; (пропущена скобка), опечатки вроде if ($x = 1) (используется = вместо == в if), опечатки в именах переменных, функций и классов, обращение к несуществующей переменной.

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

    Smoke testing — это тесты, которые проверяют общую работоспособность программы. Например, для сайта скрипт тестирования может обходить страницы из подготовленного списка и проверять, что они вообще загружаются и содержат хоть какой-то текст (например, название сайта). Также, можно жать на все кнопки на странице и проверять, что при этом не возникает яваскрипт-ошибок.

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

    Вот ссылка про нагрузочное тестирование в Яндексе: http://habrahabr.ru/company/yandex/blog/202020/

    Регрессионное тестирование — это тестирование, что ранее обнаруженная ошибка больше не встречается. Если ты нашел какой-то баг, ты пишешь тест на него и больше он не останется незамеченным.

    Тестирование на основе скриншотов — статья от Яндекса http://habrahabr.ru/company/yandex/blog/200968/

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

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

    Сам phpUnit распространяется в виде одного файла phpunit.phar, и ты запускаешь его командой вроде

    Чтобы увидеть список всех доступных опций, можно набрать

    (если ты не работал с командной строкой и плохо понимаешь, о чем речь, прочти мой гайд по командной строке).

    Тесты для phpunit хранятся в файлах (их может быть много, и при желании их можно раскладывать по папкам), каждый файл содержит 1 класс, унаследованный от встроенного в phpUnit класса PHPUnit_Framework_TestCase . А этот класс может содержать 1 или больше методов с конкретными тестовыми примерами. Давай напишем тест, проверяющий работу php-функции count (она возвращает число элементов в массиве, и тестом мы проверим что она делает это правильно).

    Каждый метод, имя которого начинается с test , будет выполнен. Также, ты можешь добавить методы setUp и tearDown , которые будут вызываться до и после каждого теста.

    Встроенный в phpUnit метод assertEquals проверяет что второй аргумент равен первому, а если это не так, то выведется соотвтетсвующее собщение и тест будет помечен как проваленный. Там еще есть много других методов, начинающихся с assert, для проверки массивов, объектов, проверки что функция выкидывает исключение или генерирует ошибку.

    Запустить тест можно командой

    PhpUnit выполнит его и выведет результаты. Если у тебя много тестов, можно указать только имя папки и phpUnit сам найдет все файлы в ней, имена которых заканчиваются на Test.php и выполнит.

    Если ты тестируешь существующий код, то часто что-то требуется сделать до выполнения тестов, например, подключить автозагрузчик или задать значение какой-то переменной. Для этого можо создать файл, например, bootstrap.php. Если ты используешь композер, то подключение автозагрузки делается одной строчкой:

    Если нет, то потребуется чуть больше кода. После этого, мы можем указать phpunit подключить этот файл с помощью опции —bootstrap :

    Обычно для тестов создают папку с названием tests в корне проекта.

    Настройки для phpunit также можно задать не опциями, а в XML-файле phpunit.xml (что такое XML? wiki). Формат файла описан в мануале. Это удобнее,так как их в этом случае не надо печатать в командной строке. Вот как будет выглядеть файл для примера выше:

    Соответственно, для запуска тестов достаточно набрать

    Подвох: phpunit при выполнении тестов перехватывает и скрывает все, что выводится с помощью echo и аналогичных функций. Если тебе надо что-то вывести для отладки, используй конструкцию вроде var_dump($x); die(); .

    Ссылки про phpUnit:

    phpunit интегрируется в IDE:

    • Eclipse PDT через плагин MakeGood или PTI (англ.)
    • Netbeans (я подозреваю, что можно обойтись без установки PEAR, достаточно указать путь к phpunit.phar)
    • PhpStorm, мануал (англ.) — достаточно нажать в настройках кнопку скачивания phpunit.phar

    В этом случае ты можешь запускать и просматривать результаты тестов прямо в IDE. Но учиться лучше с использованием командной строки.

    Cайт codeception. Этот фреймворк заточен на написание API и GUI тестов (хотя он включает в себя phpunit и может выполнять его тесты, но удобнее их хранить отдельно). Он может работать как с примитивным html-браузером на основе Symfony BrowserKit (не интерпретирующим CSS и JS), так и с PhantomJS и Selenium. Также, он может использоваться для «функционального» тестирования, то есть вызова контроллеров напрямую (не через запрос на веб-сервер). Для этого у него есть плагины к разным популярным фреймворкам. Причем синтаксис скриптов для всех этих случаев примерно одинаков.

    Подвох: codeception выполняет каждый скрипт 2 раза (это было раньше, сейчас это исправлено). Если ты пытаешься в скрипте определить функцию или класс, ты получишь из-за этого ошибку. Создавай классы и функции во внешнем файле, подключаемом через require_once .

    Codeception, как и phpUnit, распространяется в виде одного файла codecept.phar. После скачивания можно запустить команду

    Чтобы он создал нужную структуру папок для тестов.

    Вот как может выглядеть скрипт проверки формы регистрации. Код на нем похож на текст на английском языке и видимо вдохновлен behat (который вдохновлен рубиевским cucumber).

    Команда, чтобы запустить этот скрипт, есть на сайте.

    Как я уже писал выше, этот скрипт может фактически работать с использованием разных уровней эмуляции браузера (первый способ самый быстрый и простой, последний самый точный и мощный, но медленный):

    • вызывать напрямую контроллер, если твое приложение использует поддерживаемый фреймворк (поддерживаются ZF1-2, Yii1-2, Symfony2, Silex. Slim не поддерживается, никто не желает написать плагин и выложить в опенсорс?)
    • используя PhpBrowser, то есть скачивание HTML-страниц через HTTP
    • контролируя PhantomJS через WebDriver
    • контролируя реальный браузер через Selenium

    В codeception есть много удобных вспомогательных функций для заполнения форм, отправки файлов, поиска элементов на странице с помощью XPath и CSS селекторов, работы с куками, заголовками, аякс-запросами, всплывающими окнами, ифреймами, снятия скриншотов, навигации. Эти функции разбиты на модули (например, модуль для тестирования АПИ, модуль для работы с базой данных), и ты можешь писать свои или расширять существующие модули через наследование.

    Skipfish (англ.) — инструмент от Google, который может использоваться для поиска ошибок на сайте и заодно для нагрузочного тестирования. Он обходит все страницы, начиная со стартовой и перемещаясь по ссылкам, и позволяет обнаруживать битые ссылки (в том числе на картинки, CSS и JS файлы). Также, он умеет отправлять запросы со случайно сгенерированными данными и пытается искать явные XSS/SQL уязвимости. Он работает очень быстро (если конечно сайт может отвечать быстро).

    Skipfish — это не совсем средство автоматического тестирования, так как результаты работы выдаются в виде html-отчета, но он может быть полезен например для поиска ошибок на существующем сайте.

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

    PhantomJS — это браузерный движок (используется Webkit — тот же, что используется в Safari, Opera, Яндекс-браузере и старых версиях Хрома), которым можно управлять с помощью скриптов на яваскрипте. Это headless браузер, то есть он не выводит никаких окон (и вообще не требует наличия видеокарты и дисплея), а работает как приложение командной строки. Он кроссплатформенный и его можно запускать, например, автоматически на линукс сервере. Он умеет переходить по страницам, загружать CSS/JS (при желании и картинки), делать скриншоты, выполнять произвольный JS код в контексте страницы.

    Также, для PhantomJS есть плагин ghostdriver (WebDriver), который позволяет подсоединиться к программе извне и управлять ей. Он использует протокол Selenium, и с его помощью PhantomJS можно управлять из codeception.

    Тесты по идее можно писать на яваскрипте, скармливая их напрямую PhantomJS, но удобнее использовать какую-нибудь библиотеку работающую поверх него, например, Casper.js (это если ты готов писать тесты на явскрипте, если на PHP, то стоит использовать codeception + PhantomJS через WebDriver).

    Для повышения скорости работы теста стоит отключить загрузку картинок, если они не требуются для теста.

    Статьи по использованию PhantomJS с codeception наверно нетрудно нагуглить.

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

    Selenium сервер написан на Яве, потому она понадобится чтобы его запустить. Поддерживаются браузеры Firefox, IE (6-11), Safari на OS X, Opera 12 (старая Опера), Chrome.

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

    Так как настроить окружение для запуска тестов сложно, есть коммерческие сервисы (например saucelabs) которые за плату выполняют selenium-тесты на нужных браузерах и возвращают результат. Они предоставляют API с помощью которого тесты можно запускать автоматически и умеют отслеживать изменения в репозитории, тестируя код при каждом новом коммите.

    Тесты в браузере содержат подвохи: например, когда ты программно нажимаешь кнопку, браузеру нужно время, чтобы выполнить привязанный к ней яваскрипт, обработать изменения в DOM, перерисовать экран. Если твой скрипт не будет дожидаться этого, а попробует сразу после нажатия кнопки проверить изменения на экране, он может их не увидеть. В некоторых статьях ты можешь увидеть совет вроде «делать паузу N мс после каждого шага», но это плохие советы. Во-первых, нет гарантий, что действие выполнится за эти N мс, во-вторых, это сильно тормозит тесты. Лучше, как советует Яндекс, в таких случаях периодически проверять появление определенного элемента на странице.

    Также, не так просто проверить, что элемент видим. Ведь в CSS много свойств ( opacity , visisbility , display ), которыми можно его скрыть, плюс он может быть помещен за пределами экрана.

    Тесты в браузере могут быть хрупкими. У браузера может выскочить окно обновления, может произойти ошибка при загрузке какого-то внешнего ресурса, в общем, сложностей тут много. А кто говорил, что будет легко?

    Также, Selenium содержит плагин к фаерфоксу (Selenium IDE), который позволяет записывать действия пользователя и генерировать из них тест (то есть повторять эти действия позже), но он, как я понимаю, довольно слабый и генерирует тяжелочитаемый код на своем странном языке. Гораздо лучше управлять Selenium из codeception и писать тесты на PHP.

    Mocha, гитхаб (читается «мока», кто бы поверил) — фреймворк для тестирования яваскрипт-кода, например приложений. Он использует подход BDD.

    Это фреймворк для написания тестов, имитирующий браузер (поддерживается CSS/JS) за счет JSDOM, то есть реализации DOM под Node.JS. Внутри он также использует вышеупомянутый Mocha. Его можно использовать для тестирования верстки и яваскрипта. Ссылка: http://zombie.labnotes.org/ http://zombie.js.org/

    Вот пример теста, тестирующий позитивный сценарий успешной регистрации на некоем воображаемом сайте (код я не проверял, так что могут быть небольшие ошибки). Этот код запускается под Node.JS:

    Jasmine (англ.) — это тоже фреймворк для тестирования JS-кода с уклоном в BDD (Behaviour-Driven Development). Если ты забыл, то идея BDD в том, что мы до разработки описываем требования (спецификацию) к коду в виде тестов. Он позволяет писать простые, легко читаемые тесты. Запускать тесты можно как через браузер (если ты хочешь смотреть релуьтаты глазами), так и из командной строки с помощью Node.JS (если проверку надо автоматизировать). Если твои тесты используют взаимодействие с DOM (или какие-то другие компоненты браузера) то для запуска под Node.JS придется подключить упомянутый выше JSDOM. Вот пример простого теста на Jasmine, которым мы протестируем функцию вычисления квадратного корня Math.sqrt() :

    Если не обращать внимание на некоторую нечитаемость текста из-за смеси русского и английского, то в общем код теста действительно напоминает список требований к функции. Мы описываем конкретные тестовые случаи с помощью конструкций expect(. ).toBe(. ) где указываем пример выполняемого кода и ожидаемый результат.

    Вот результат выполнения этих тестов в браузере, где Jasmine выводит список пройденных проверок:

    Jasmine расширяемый и ты можешь дописывать свои проверяльщики (matchers) и свой код для вывода результатов в удобном тебе виде.

    Чтобы не запускать тесты каждый раз руками, используют CI Server вроде Teamcity, Jenkins или Hudson (статья от Яндекса: http://habrahabr.ru/company/yandex/blog/237017/ ). Но эти системы надо устанавливать и настраивать, в то время как Travis это сервис, который сам будет подсоединяться к твоему гитхаб репозиторию, брать оттуда изменения и прогонять тесты. Для open source проектов на github Travis CI бесплатен.

    Статья с теорией и определениями разных терминов (что такое тест-кейс, какие бывают уровни тестирования, серьезность дефекта, виды тестирования ит.д.): Тестирование. Фундаментальная теория

    На хабре хорошие статьи про тестирование в блогах Яндекса и Баду:

    Вот примеры разных open source проектов c тестами (кстати, многие из них используют travis CI):

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

    Ты форкаешь тот, который тебе больше нравится (или меньше не нравится), и мы пишем под него юнит-тесты на phpUnit и интерфейсные тесты на codeception.

    Если тебе не нравится файлообменник, можно потестировать что-то еще (если есть идеи небольшого приложения на PHP, требующего тестов — напишите).

    Прежде чем писать тесты, надо составить план тестирования (что мы тестируем и как). Мы сначала определяем, какие возможности предоставляет приложение, а потом для каждой пишем несколько тест кейсов. Не забывай, что надо тестировать как позитивные, так и негативные сценарии. Вот примерный план, 1 пункт = 1 тест.

    • Загрузка файла
      • Загрузить файл и убедиться что он загрузился
      • Отправить пустую форму без файла
      • Загрузить слишком большой файл
      • Загрузить файл c utf-8 символами в названии и проверить что все отображается корректно
    • Страница файла
      • Загрузить файл и убедиться что он доступен для скачивания. Скачиваемый файл совпадает с загруженным
      • Убедиться что выводится правильная информация о файле
    • Медиаданные
      • Загрузить картинку и убедиться что превьюшка выводится и она совпадает с картинкой
      • Загрузить картинку неподдерживаемого типа вроде tif/bmp
      • Загрузить аудиофайл и убедиться что доступен плеер
      • Загрузить видеофайл и увидеть плеер
    • Комментарии
      • Отправить комментарий
      • Отправить пустой комментарий
      • Отправить комментарий с HTML кодом
      • Отправить комментарий-ответ
    • Последние файлы
      • Загрузить 3 файла и проверить что они отображаются в правильном порядке с правильными ссылками

    Также, стоит написать тест на codeception который обходит сайт и проверяет отстутвие битых ссылок.

    В качестве базы для тестов стоит использовать in-memory mysql базу.

    Если у тебя есть какие-то дополнения к плану, можно их внести.

    Что тестировать юнит-тестами? Ими можно тестировать например функцию валидации (комментария к примеру), а также функции вроде форматирования размера файла (которые выводят его в виде «12Мб»).

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

    Модульное тестирование в PHP?

    Я подумывал о написании модульных тестов для моих проектов PHP/MySQL.

    Меня беспокоит то, как я могу проверить такие вещи, как проверка формы, функции ajax и возможности пользовательского интерфейса (например, ссылки на ссылки). Кроме того, я не хочу, чтобы тесты зависели от одного аспекта пользовательского интерфейса, поэтому, если бы я переместил одну ссылку, все тесты сломались бы.

    Есть ли у кого-нибудь идеи/предложения о том, какие части кода я должен unit test, примерно сколько времени нужно потратить на модульное тестирование и любые другие предложения/советы?

    Единичные тесты, по крайней мере, с точки зрения разработки веб-приложений, не могут действительно охватывать дизайн пользовательского интерфейса. Какие модульные тесты могут помочь вам в тестировании ввода/вывода всех ваших методов управления и/или любых однотонных/глобальных функций, которые вы используете. Итак, если вы хотите получить покрытие unit test при проверке формы, убедитесь, что ваше приложение структурировано таким образом, что вы можете проверить свою функцию проверки независимо от пользовательского интерфейса.

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

    Вы никогда не станете на 100% уверены, что невинное изменение не будет проверять ваши тесты Selenium, но есть шаги, которые вы можете предпринять во время разработки, чтобы свести это к минимуму. Selenium различные способы для «целевого» элемента, на который он нажмет или введите. Вы хотите использовать таргетинг, который является настолько конкретным, насколько это возможно, поэтому постарайтесь предоставить всем вашим (X) HTML-узлам уникальный идентификационный элемент. Это гарантирует, что селен щелкнет ссылку независимо от того, где она попадает на страницу.

    Если уникальный идентификатор невозможен, попробуйте обернуть общие элементы с помощью некоторого HTML-элемента, который имеет уникальный идентификатор, а затем использовать возможности selenium xpath tareting, чтобы сказать «щелкните ссылку, содержащую текст» signup «, который находится в div с идентификатором» foo «или нажмите третью ссылку в div с идентификатором» foo».

    Если это не очевидно из приведенных выше элементов, не полагаться на выбор таргетинга, который будет выбирать Selenium IDE (или другие рекордеры). Их цель — обеспечить, чтобы выполнение теста на этой точной странице дало вам те же результаты, которые могут дать вам очень хрупкий xpath. Всегда проверяйте тесты, которые вы записываете, и фиксируйте дрянные таргетинг.

    Здесь нет простых ответов. Если бы это было так, веб-приложения были бы намного менее ошибочными.

    Добавить комментарий