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

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

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

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

Этот пост посвящен одному типу тестов, и я думаю, что он самый интересный.

Приемочное тестирование

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

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

Однако в этой тестовой библиотеке используется так называемый браузер PHP, это в основном искатель DOM, который считывает ответ, исходящий от сервера, и имитирует взаимодействие с ним пользователя, это быстро и нормально в большинстве случаев, но у него есть серьезный недостаток. не может взаимодействовать с интерфейсами, управляемыми JavaScript, а это значит, что мы не можем полагаться на него при тестировании наших интерфейсов на базе Vue.js.

Selenium WebDriver

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

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

Официальный драйвер JavaScript

Вот пример использования:

var webdriver = require('selenium-webdriver'); 
var driver = new webdriver.Builder().forBrowser('chrome').build(); 
driver.get('themsaid.com'); driver.findElement(webdriver.By.css('#projectLink')).click(); driver.wait(webdriver.until.until.titleIs('Project'), 1000); driver.quit();

Этот тест открывает themsaid.com в Chrome, нажимает на элемент и ожидает увидеть изменение заголовка страницы. Вы можете запустить этот тест с помощью этой команды:

node tests/themsaidtest.js

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

Nightwatch.js

module.exports = { 
  'Check for element' : function (client) { 
    client.url('themsaid.com') 
          .click('#projectLink') 
          .assert.title('Project') 
          .end(); 
  } 
};

Намного лучший синтаксис, Nightwatch.js также имеет встроенную программу запуска тестов, все, что вам нужно сделать для запуска тестов, это:

nightwatch

Вы также можете запускать одиночные тесты, по группам и по тегам. Так что да, это был наш фреймворк, однако есть много других альтернатив, таких как Dalek.js и WebDriver i / o.

Дело в тестовых фреймворках на основе Javascript

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

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

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

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

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

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

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

Расширение PHPUnit Selenium

Используя расширение selenium для PHPUnit, мы можем фактически установить начальные данные и записать шаги теста в одном месте, например, мы создаем пользователя с использованием фабрик моделей, а затем мы используем его адрес электронной почты и пароль для тестирования нашего экрана входа в систему:

putenv('DB_DATABASE=test_database'); 
$user = factory(User::class)->create(['password'=>'500']); 
$this->url('/login'); 
$this->byName('username')->value($user->username); 
$this->byName('password')->value(500); 
$this->byCssSelector('form.loginForm')->submit(); 
$this->assertRegExp('/Welcome, '.$user->name.'!/', 
$this->byCssSelector('h2.welcomeMessage')->text());

Все, что вам нужно сделать, это загрузить это расширение через композитор и скачать Selenium Server.

composer require --dev phpunit/phpunit-selenium

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

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

Codeception

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

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

$I->wantTo('create wiki page'); 
$I->amOnPage('/'); 
$I->click('Pages'); 
$I->click('New'); 
$I->see('New Page'); 
$I->fillField('title', 'Hobbit'); 
$I->fillField('body', 'By Peter Jackson'); 
$I->click('Save'); 
$I->see('page created');
$I->see('Hobbit','h1');
$I->seeInCurrentUrl('pages/hobbit');

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

Установка и настройка

Чтобы начать использовать Codeception, нам нужно вытащить его через композитор:

php composer.phar require "codeception/codeception:*"

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

php vendor/bin/codecept bootstrap

Это создаст несколько папок в вашем тестовом каталоге Laravel, в нашем случае мы удалили все, кроме папки принятия, поскольку нам нужен Codeception только для проведения приемочного тестирования, мы также изменили расположение _output, _data и _support, чтобы они находились внутри папки принятия. .

Чтобы начать использовать Codeception, нам также необходимо запустить Selenium Server, так что продолжайте и загрузите его, а также загрузите Драйвер Chrome, если вы тестируете с помощью Chrome.

Чтобы все было в одном месте, мы создали папку bin внутри tests / accept, где мы храним исполняемые файлы selenium server и chrome driver.

Мы изменили конфигурацию в codecept.yml на следующую:

actor: Tester
paths:
 tests: tests
 log: tests/acceptance/_output
 data: tests/acceptance/_data
 support: tests/acceptance/_support
 envs: tests/_envs
settings:
 bootstrap: _bootstrap.php
 colors: true
 memory_limit: 1024M
extensions:
 enabled:
 — Codeception\Extension\RunFailed

Мы также изменили tests / accept.suite.yml, чтобы использовать драйвер Chrome:

class_name: AcceptanceTester
modules:
 enabled:
   — WebDriver:
       url: http://project-name.dev
       browser: chrome
   — \Helper\Acceptance

До сих пор Codeception ничего не знает о laravel, чтобы исправить то, что мы включили следующее внутри tests / accept / _bootstrap.php, этот файл будет выполнен перед набором тестов:

require __DIR__.’/../../bootstrap/autoload.php’;
$app = require __DIR__.’/../../bootstrap/app.php’;
putenv(‘DB_DATABASE=testing_database’);
$app->instance(‘request’, new \Illuminate\Http\Request);
$app->make(‘Illuminate\Contracts\Http\Kernel’)->bootstrap();

Обнаружение тестов Codeception в laravel

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

Вот как выглядит метод handle в промежуточном программном обеспечении:

public function handle($request, Closure $next)
{
  if (app()->isLocal() && isset($_COOKIE[‘selenium_request’])) {
      config([‘database.default’ => ‘mysql_testing’]);
      if (isset($_COOKIE[‘selenium_auth’])) {
          Auth::loginUsingId((int) $_COOKIE[‘selenium_auth’]);
      }
  }
  return $next($request);
}

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

Инициализация тестов Codeception

Как мы упоминали ранее, нам нужно установить cookie для каждого нового сеанса Selenium, для этого мы можем добавить настраиваемое действие в accept / _support / AcceptanceTester.php:

class AcceptanceTester extends \Codeception\Actor
{
   use _generated\AcceptanceTesterActions;
   public function init($loginUsingId = null)
   {
       $this->amOnPage(‘/’);
 
       $this->setCookie(‘selenium_request’, ‘true’);
       if ($loginUsingId) {
           $this->setCookie(‘selenium_auth’, (string) $loginUsingId);
       }
   }
}

Другое решение, упомянутое @ christophe.graniczny в ответах, - это настроить виртуальный хост для установки значения среды на APP_ENV = codeception для определенного домена, а затем использовать этот домен для приемочных испытаний. Затем вы можете добавить файл среды .env.codeception, который будет использоваться программой codeception.

Это почти все, что нам нужно сделать, теперь давайте напишем наш первый тест.

Первый тест

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

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

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

Перед запуском тестов нам нужно запустить селен-сервер:

java -jar tests/acceptance/bin/selenium.jar -Dwebdriver.chrome.driver=tests/acceptance/bin/chromedriver

Мы оставляем этот экземпляр оболочки открытым и запускаем наши тесты с новой вкладки:

vendor/bin/codecept run

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

Заключение

Если вы тестируете интерфейсы, не основанные на javascript, вы можете использовать встроенную в Laravel библиотеку тестирования на основе браузера PHP, она очень мощная, и API также очень удобочитаем. Однако, если вам нужно протестировать интерфейсы, управляемые javascript, вам следует использовать селен.

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