Статьи

Функциональное тестирование в Symfony2

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

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

Функциональное тестирование

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

В некоторой степени я рассмотрел юнит-тесты в моей предыдущей статье .

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

Если мы посмотрим на тестирование класса Pagination в моей предыдущей статье , мы очень ясно увидим, как подделаны разные сценарии и проверен результат:

            $paginator=new Paginator(2, 101, 10);
            $pages=$paginator->getTotalPages();
            $this->assertEquals($pages, 11);
            $list=$paginator->getPagesList();
            $this->assertEquals($list, array(1,2,3,4,5));

            $paginator=new Paginator(7, 101, 10);
            $list=$paginator->getPagesList();
            $this->assertEquals($list, array(5,6,7,8,9));

            $paginator=new Paginator(10, 101, 10);
            $list=$paginator->getPagesList();
            $this->assertEquals($list, array(7,8,9,10,11)); 

Функциональное тестирование отличается. Мы не смотрим на «правильность» отдельной функции, которая должна быть проверена модульным тестом, а на общую картину. Функциональное тестирование ответило на вопрос: хорошо ли работает наше приложение в том смысле, что оно отображает правильный контент, соответствует взаимодействию пользователя и т. Д.?

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

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

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

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

  • У нас должно быть всего 101 книга в нашей библиотеке.
  • Последняя собранная книга должна иметь дату покупки 1970-01-01, а ее автор — « Специальный ».
  • У нас должно быть 2 сборника статей (заголовков) для чтения моей книги, а последний обзор должен называться « Обзор 2 ».

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

Файл тестирования для проверки вышеуказанных условий (или «утверждений») находится по адресу: src/tr/rsywxBundle/Tests/Controller/DefaultControllerTest.php

 <?php

namespace tr\rsywxBundle\Tests\Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class DefaultControllerTest extends WebTestCase
{

    public function testIndex()
    {
        $client = static::createClient();

        $crawler = $client->request('GET', '/');

//        $text = $crawler->text();
//        $fp = fopen('index.txt', 'w');
//        fwrite($fp, $text);
//        fclose($fp);

        $this->assertTrue($crawler->filter('html:contains("1970-01-01")')->count() == 1);
        $this->assertTrue($crawler->filter('html:contains("101 books")')->count() == 1);
        $this->assertTrue($crawler->filter('html:contains("by Special")')->count() == 1);
        $this->assertTrue($crawler->filter('html:contains("2 articles")')->count() == 1);
        $this->assertTrue($crawler->filter('html:contains("Review 2")')->count() == 1);

        //We click a link and go to the detail page
        $links=$crawler->selectLink('Special Book Title')->links();
        $link=$links[0];

        $crawler=$client->click($link);
}

Мы создаем HTTP-клиента и создаем сканер, имитирующий действия пользователя. В этом тестовом примере мы хотим посетить домашнюю страницу сайта, поэтому мы используем $crawler = $client->request('GET', '/'); ,

Затем, чтобы убедиться, что содержимое страницы содержит эту ключевую информацию, определенную ранее, мы делаем несколько утверждений, используя assertTrue Например, чтобы перевести «У нас должно быть всего 101 книга в нашей библиотеке» на:

 $this->assertTrue($crawler->filter('html:contains("101 books")')->count() == 1);

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

Чтобы запустить тест, введите в окне терминала следующую команду:

 php phuunit.phar -c app/

Аргумент «-c app /» просто говорит PHPUnit использовать конфигурацию, найденную в каталоге app

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

Давайте проверим и некоторые другие аспекты. Как насчет ссылок? Они делают то, что должны (с точки зрения URI и переноса нас на нужную страницу)?

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

Давайте добавим еще несколько строк кода:

         //We click a link and go to the detail page
        $links=$crawler->selectLink('Special Book Title')->links();
        $link=$links[0];

        $crawler=$client->click($link);

        $this->assertTrue($crawler->filter('html:contains("ISBN: 123456789")')->count() == 1);
        $this->assertTrue($crawler->filter('html:contains("tag1")')->count() == 1);
        $this->assertTrue($crawler->filter('html:contains("tag2")')->count() == 1);
        $this->assertTrue($crawler->filter('html:contains("tag3")')->count() == 1);
        $this->assertTrue($crawler->filter('html:contains("tag4")')->count() == 1);
        $this->assertTrue($crawler->filter('html:contains("tag5")')->count() == 1);

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

Делая вышеупомянутые дополнительные утверждения, мы уверены, что мы просматриваем книгу («специальную» книгу, которую мы создали ранее), как указано ее ISBN и 5 тегами. Мы также уверены, что ссылка на главной странице правильно переносит нас на страницу сведений о книге, на которой показана книга, указанная на главной странице (как последняя собранная книга).

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

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

Вывод

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

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