Может войдёшь?
Черновики Написать статью Профиль

Тестирование

перевод документация 5.х

  1. 1. Введение
    1. 1.1. Тестовая среда
    2. 1.2. Определение и выполнение тестов
  2. 2. Тестирование приложения
    1. 2.1. Взаимодействие с приложением
    2. 2.2. Тестирование JSON API
    3. 2.3. Сессии / Аутентификация
    4. 2.4. Отключение посредников
    5. 2.5. Свои HTTP-запросы
    6. 2.6. Проверки PHPUnit (assertions)
  3. 3. Работа с базами данных
    1. 3.1. Сброс базы данных после каждого теста
    2. 3.2. Фабрики моделей
  4. 4. Заглушки
    1. 4.1. Заглушки событий
    2. 4.2. Заглушки задач
    3. 4.3. Заглушки фасадов
  5. 5. Вспомогательные методы
  6. 6. Обновление приложения
Этот перевод актуален для англоязычной документации на (ветка 5.1) и (ветка 5.0). Опечатка? Выдели и нажми Ctrl+Enter.

Введение

Laravel построен с учётом тестирования. Фактически, поддержка PHPUnit доступна по умолчанию, а файл phpunit.xml уже настроен для вашего приложения. Также фреймворк содержит удобные методы для полноценного тестирования ваших приложений.

Папка tests содержит файл с примером теста ExampleTest.php. После установки нового приложения Laravel просто выполните команду shphpunit для запуска ваших тестов.

Тестовая среда

При выполнении тестов Laravel автоматически задаст настройки среды testing. Также при тестировании Laravel автоматически настроит сессии и кэш на драйвер array, а значит данные сессий и кэша не сохранятся при тестировании.

При необходимости вы можете создать другие настройки для тестовой среды. Переменные среды testing можно настроить в файле phpunit.xml.

Определение и выполнение тестов

Для создания теста используйте Artisan-команду shmake:test:

shphp artisan make:test UserTest

Эта команда поместит новый класс UserTest в папку tests. Далее вы можете объявлять методы тестов как вы обычно объявляете их для PHPUnit. Для запуска тестов просто выполните команду shphpunit в терминале:

PHP
<?php

use Illuminate\Foundation\Testing\WithoutMiddleware;
use 
Illuminate\Foundation\Testing\DatabaseMigrations;
use 
Illuminate\Foundation\Testing\DatabaseTransactions;

class 
UserTest extends TestCase
{
  
/**
   * Пример базового теста.
   *
   * @return void
   */
  
public function testExample()
  {
    
$this->assertTrue(true);
  }
}

Если вы определили собственный метод PHPsetUp(), не забудьте вызвать PHPparent::setUp().

Тестирование приложения

Laravel предоставляет очень удобный API для создания HTTP-запросов к вашему приложению, проверки вывода, и даже заполнения форм. Например, посмотрим на файл ExampleTest.php в папке tests:

PHP
<?php

use Illuminate\Foundation\Testing\WithoutMiddleware;
use 
Illuminate\Foundation\Testing\DatabaseTransactions;

class 
ExampleTest extends TestCase
{
  
/**
   * Пример базового теста функции.
   *
   * @return void
   */
  
public function testBasicExample()
  {
    
$this->visit('/')
         ->
see('Laravel 5')
         ->
dontSee('Rails');
  }
}

Метод PHPvisit() делает GET-запрос в приложение. Метод PHPsee() объявляет, что мы должны увидеть данный текст в отклике приложения. Метод PHPdontSee() объявляет, что данный текст не возвращается в отклике приложения. Это самый базовый тест приложения в Laravel.

Взаимодействие с приложением

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

Нажатие ссылок

В этом тесте мы сделаем запрос в приложение, «нажмём» ссылку в возвращённом отклике, а затем проверим, что оказались на нужном URI. Например, предположим, что в нашем отклике есть ссылка с текстом «О нас»:

xml<a href="/about-us">О нас</a>

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

PHP
public function testBasicExample()
{
  
$this->visit('/')
       ->
click('О нас')
       ->
seePageIs('/about-us');
}

Работа с формами

Также Laravel предоставляет несколько методов для тестирования форм. Методы PHPtype(), PHPselect(), PHPcheck(), PHPattach() и PHPpress() позволяют вам взаимодействовать со всеми элементами ввода на ваших формах. Например, представим, что на странице регистрации в приложении есть такая форма:

xml<form action="/register" method="POST">
  {!! csrf_field() !!}

  <div>
    Name: <input type="text" name="name">
  </div>

  <div>
    <input type="checkbox" value="yes" name="terms"> Accept Terms
  </div>

  <div>
    <input type="submit" value="Register">
  </div>
</form>

Мы можем написать тест для заполнения этой формы и проверки результата:

PHP
public function testNewUserRegistration()
{
  
$this->visit('/register')
       ->
type('Taylor''name')
       ->
check('terms')
       ->
press('Register')
       ->
seePageIs('/dashboard');
}

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

Метод Описание
$this->type($text, $elementName)Ввести текст в данное поле
$this->select($value, $elementName)Выбрать радио-кнопку или выпадающее поле
$this->check($elementName)Поставить чекбокс
$this->attach($pathToFile, $elementName)Прикрепить файл к форме
$this->press($buttonTextOrElementName)Нажать кнопку с заданным текстом или именем

Работа с вложениями

Если на вашей форме есть элементы ввода типа file, вы можете прикрепить файлы к форме методом PHPattach():

PHP
public function testPhotoCanBeUploaded()
{
  
$this->visit('/upload')
       ->
name('File Name''name')
       ->
attach($absolutePathToFile'photo')
       ->
press('Upload')
       ->
see('Upload Successful!');
}

Тестирование JSON API

Также Laravel предоставляет несколько вспомогательных функций для тестирования JSON API и их откликов. Например, методы PHPget(), PHPpost(), PHPput(), PHPpatch() и PHPdelete() используются для выполнения различных HTTP-запросов. Вы также легко можете передать данные и заголовки в эти методы. Для начала давайте напишем тест, выполняющий POST-запрос к /user и проверяющий, что данный массив возвращается в формате JSON:

PHP
<?php

class ExampleTest extends TestCase
{
  
/**
   * Пример базового теста функции.
   *
   * @return void
   */
  
public function testBasicExample()
  {
    
$this->post('/user', ['name' => 'Sally'])
         ->
seeJson([
           
'created' => true,
         ]);
  }
}

Метод PHPseeJson() конвертирует данный массив в JSON, а затем проверяет, что фрагмент JSON появляется где-либо внутри полного JSON-отклика, возвращаемого приложением. Поэтому, если в нём будут ещё и другие свойства, этот тест всё равно будет пройден успешно, так как данный фрагмент присутствует в отклике.

Проверка точного совпадения JSON

Если вы хотите проверить точное совпадение данного массива с возвращённым из приложения JSON, вам надо использовать метод PHPseeJsonEquals():

PHP
<?php

class ExampleTest extends TestCase
{
  
/**
   * Пример базового теста функции.
   *
   * @return void
   */
  
public function testBasicExample()
  {
    
$this->post('/user', ['name' => 'Sally'])
         ->
seeJsonEquals([
           
'created' => true,
         ]);
  }
}

Сессии / Аутентификация

В Laravel есть несколько функций для работы с сессиями во время тестирования. Сначала вы можете задать данные сессии для данного массива при помощи метода PHPwithSession(). Это полезно для загрузки сессии с данными перед выполнением тестового запроса в приложение:

PHP
<?php

class ExampleTest extends TestCase
{
  public function 
testApplication()
  {
    
$this->withSession(['foo' => 'bar'])
         ->
visit('/');
  }
}

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

PHP
<?php

class ExampleTest extends TestCase
{
  public function 
testApplication()
  {
    
$user factory(App\User::class)->create();

    
$this->actingAs($user)
         ->
withSession(['foo' => 'bar'])
         ->
visit('/')
         ->
see('Hello, '.$user->name);
  }
}

Отключение посредников

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

PHP
<?php

use Illuminate\Foundation\Testing\WithoutMiddleware;
use 
Illuminate\Foundation\Testing\DatabaseTransactions;

class 
ExampleTest extends TestCase
{
  use 
WithoutMiddleware;

  
//
}

Если захотите отключить посредников только для некоторых тестовых методов, можете вызвать метод PHPwithoutMiddleware() из тестовых методов:

PHP
<?php

class ExampleTest extends TestCase
{
  
/**
   * Пример базового теста функции.
   *
   * @return void
   */
  
public function testBasicExample()
  {
    
$this->withoutMiddleware();

    
$this->visit('/')
         ->
see('Laravel 5');
  }
}

Свои HTTP-запросы

Если вы хотите сделать свой HTTP-запрос в приложение и получить полный объект Illuminate\Http\Response, используйте метод PHPcall():

PHP
public function testApplication()
{
  
$response $this->call('GET''/');

  
$this->assertEquals(200$response->status());
}

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

PHP
$response $this->call('POST''/user', ['name' => 'Taylor']);

Проверки PHPUnit (assertions)

Laravel предоставляет несколько assert-методов для тестов PHPUnit:

Метод Описание
->assertResponseOk();Проверка того, что клиентский отклик имеет статус ОК
->assertResponseStatus($code);Проверка того, что клиентский отклик имеет указанный статус.
->assertViewHas($key, $value = null);Проверка того, что представление в отклике содержит данный кусок привязанных данных.
->assertViewHasAll(array $bindings);Проверка того, что представление содержит данный список привязанных данных.
->assertViewMissing($key);Проверка того, что в представлении в отклике отсутствует данный кусок привязанных данных.
->assertRedirectedTo($uri, $with = []);Проверка того, что клиент был переадресован по данному URI
->assertRedirectedToRoute($name, $parameters = [], $with = []);Проверка того, что клиент был переадресован по данному маршруту
->assertRedirectedToAction($name, $parameters = [], $with = []);Проверка того, что клиент был переадресован к данному действию
->assertSessionHas($key, $value = null);Проверка того, что в сессии есть данное значение.
->assertSessionHasAll(array $bindings);Проверка того, что в сессии есть данный список значений.
->assertSessionHasErrors($bindings = [], $format = null);Проверка того, что в сессии есть привязанные ошибки.
->assertHasOldInput();Проверка того, что в сессии есть введённые ранее данные.
+ 5.0

добавлено в 5.0 ()

Вызов контроллера из теста

Вы также можете вызвать из теста любой контроллер:

PHP
$response $this->action('GET''HomeController@index');

$response $this->action('GET''UserController@profile', ['user' => 1]);

Вам не надо указывать полное пространство имён контроллера при использовании метода PHPaction(). Укажите только ту часть, которая идёт за App\Http\Controllers.

Метод PHPgetContent() вернёт содержимое-строку ответа. Если ваш маршрут вернёт PHPView, вы можете получить его через свойство PHP$original:

PHP
$view $response->original;

$this->assertEquals('John'$view['name']);

Для вызова HTTPS-маршрута можно использовать метод PHPcallSecure():

PHP
$response $this->callSecure('GET''foo/bar');

Работа с базами данных

Также Laravel содержит различные полезные инструменты для упрощения тестирования приложений, использующих БД. Во-первых, вы можете использовать функцию PHPseeInDatabase () для проверки наличия в БД данных, подходящих по набору критериев. Например, если мы хотим проверить, что в таблице users есть запись со значением в поле email равным sally@example.com, то можем сделать следующее:

PHP
public function testDatabase()
{
    
// Сделать вызов в приложение...

    
$this->seeInDatabase('users', ['email' => 'sally@example.com']);
}

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

Сброс базы данных после каждого теста

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

Использование миграций

Один из вариантов — откатывать БД после каждого теста и мигрировать её перед следующим тестом. В Laravel есть простой типаж DatabaseMigrations, который автоматически сделает это за вас. Просто используйте типаж в классе теста:

PHP
<?php

use Illuminate\Foundation\Testing\WithoutMiddleware;
use 
Illuminate\Foundation\Testing\DatabaseMigrations;
use 
Illuminate\Foundation\Testing\DatabaseTransactions;

class 
ExampleTest extends TestCase
{
  use 
DatabaseMigrations;

  
/**
   * Пример базового теста функции.
   *
   * @return void
   */
  
public function testBasicExample()
  {
    
$this->visit('/')
         ->
see('Laravel 5');
  }
}

Использование транзакций

Другой вариант — обернуть каждый тест в транзакцию БД. И снова, в Laravel есть удобный типаж DatabaseTransactions, который автоматически сделает это за вас:

PHP
<?php

use Illuminate\Foundation\Testing\WithoutMiddleware;
use 
Illuminate\Foundation\Testing\DatabaseMigrations;
use 
Illuminate\Foundation\Testing\DatabaseTransactions;

class 
ExampleTest extends TestCase
{
  use 
DatabaseTransactions;

  
/**
   * Пример базового теста функции.
   *
   * @return void
   */
  
public function testBasicExample()
  {
    
$this->visit('/')
         ->
see('Laravel 5');
  }
}

Этот типаж обернёт в транзакцию только то соединения с БД, которое используется по умолчанию.

Фабрики моделей

При тестировании часто необходимо вставить несколько записей в БД перед выполнением теста. Вместо указания значений каждого поля тестовых данных вручную, Laravel позволяет определить набор атрибутов для каждой из ваших моделей Eloquent при помощи «фабрик». Для начала посмотрим на файл database/factories/ModelFactory.php в вашем приложении. Изначально этот файл содержит одно определение фабрики:

PHP
$factory->define(App\User::class, function (Faker\Generator $faker) {
  return [
    
'name' => $faker->name,
    
'email' => $faker->email,
    
'password' => bcrypt(str_random(10)),
    
'remember_token' => str_random(10),
  ];
});

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

Конечно, вы можете добавить свои собственные дополнительные фабрики в файл ModelFactory.php.

Множественные типы фабрик

Иногда вам необходимо иметь несколько фабрик для одного класса модели Eloquent. Например, если нужна фабрика для пользователей «Administrator» вдобавок к обычным пользователям. Эти фабрики можно определить методом PHPdefineAs():

PHP
$factory->defineAs(App\User::class, 'admin', function ($faker) {
  return [
    
'name' => $faker->name,
    
'email' => $faker->email,
    
'password' => str_random(10),
    
'remember_token' => str_random(10),
    
'admin' => true,
  ];
});

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

PHP
$factory->defineAs(App\User::class, 'admin', function ($faker) use ($factory) {
  
$user $factory->raw(App\User::class);

  return 
array_merge($user, ['admin' => true]);
});

Использование фабрик в тестах

Когда вы определили свои фабрики, вы можете использовать их в своих тестах или файлах для заполнения БД, чтобы генерировать экземпляры модели с помощью глобальной функции PHPfactory(). Давайте рассмотрим несколько примеров создания моделей. Сначала используем метод PHPmake(), который создаёт модели, но не сохраняет их в БД:

PHP
public function testDatabase()
{
    
$user factory(App\User::class)->make();

    
// Использование модели в тестах...
}

Если вы хотите переопределить некоторые из значений по умолчанию для своих моделей, вы можете передать массив значений в метод PHPmake(). Будут заменены только указанные значения, а остальные будут иметь значения, определённые в фабрике:

PHP
$user factory(App\User::class)->make([
  
'name' => 'Abigail',
 ]);

Также вы можете создать коллекцию моделей или создать модели заданного типа:

PHP
// Создать три экземпляра App\User...
$users factory(App\User::class, 3)->make();

// Создать экземпляр App\User "admin"...
$user factory(App\User::class, 'admin')->make();

// Создать три экземпляра App\User "admin"...
$users factory(App\User::class, 'admin'3)->make();

Сохранение моделей фабрики

Метод PHPcreate() не только создаёт экземпляры модели, но также сохраняет их в БД при помощи Eloquent-метода PHPsave():

PHP
public function testDatabase()
{
  
$user factory(App\User::class)->create();

  
// Использование модели в тестах...
}

Вы можете переопределить атрибуты для модели, передав массив в метод PHPcreate():

PHP
$user factory(App\User::class)->create([
  
'name' => 'Abigail',
 ]);

Добавление отношений в модели

Вы можете сохранить в БД даже несколько моделей. В данном примере мы даже прикрепим к созданным моделям отношение. При использовании метода PHPcreate() для создания нескольких моделей возвращается экземпляр коллекции, позволяя вам использовать любые удобные функции для работы с коллекцией, такие как PHPeach():

PHP
$users factory(App\User::class, 3)
         ->
create()
         ->
each(function($u) {
            
$u->posts()->save(factory(App\Post::class)->make());
          });

Заглушки

Заглушки событий

Если вы используете систему событий Laravel, то при желании можете отключить или заглушить определённые события при тестировании. Например, при тестировании регистрации пользователя вам, вероятно, не нужно вызывать обработчики всех событий UserRegistered, поскольку они могут посылать письма «добро пожаловать» и т.п.

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

PHP
<?php

class ExampleTest extends TestCase
{
  public function 
testUserRegistration()
  {
    
$this->expectsEvents(App\Events\UserRegistered::class);

    
// Тестирование кода регистрации пользователя...
  
}
}

Если вы хотите предотвратить запуск всех обработчиков событий, используйте метод PHPwithoutEvents():

PHP
<?php

class ExampleTest extends TestCase
{
  public function 
testUserRegistration()
  {
    
$this->withoutEvents();

    
// Тестирование кода регистрации пользователя...
  
}
}

Заглушки задач

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

В Laravel есть удобный метод PHPexpectsJobs(), который проверяет, что ожидаемые задачи вызываются, но при этом сами задачи выполняться не будут:

PHP
<?php

class ExampleTest extends TestCase
{
  public function 
testPurchasePodcast()
  {
    
$this->expectsJobs(App\Jobs\PurchasePodcast::class);

    
// Тестирование кода покупки подкаста...
  
}
}

Этот метод обнаруживает только те задачи, которые запускаются методами типажа DispatchesJobs. Он не обнаружит задачу, которая отправлена напрямую в PHPQueue::push.

Заглушки фасадов

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

PHP
<?php

namespace App\Http\Controllers;

use 
Cache;
use 
Illuminate\Routing\Controller;

class 
UserController extends Controller
{
  
/**
   * Показать список всех пользователей приложения.
   *
   * @return Response
   */
  
public function index()
  {
    
$value Cache::get('key');

    
//
  
}
}

Вы можете отловить вызов фасада Cache с помощью метода PHPshouldReceive(), который вернёт экземпляр объекта-заглушки Mockery. Поскольку фасады извлекаются и управляются сервис-контейнером Laravel, их намного проще тестировать, чем обычный статический класс. Например, давайте отловим наш вызов фасада Cache:

PHP
<?php

class FooTest extends TestCase
{
  public function 
testGetIndex()
  {
    
Cache::shouldReceive('get')
                ->
once()
                ->
with('key')
                ->
andReturn('value');

    
$this->visit('/users')->see('value');
  }
}

Не делайте этого для фасада Request. Вместо этого, передайте желаемый ввод вспомогательному HTTP-методу, такому как PHPcall() или PHPpost(), во время выполнения вашего теста.

+ 5.0

добавлено в 5.0 ()

Рассмотрим такое действие контроллера:

PHP
public function getIndex()
{
  
Event::fire('foo', ['name' => 'Dayle']);

  return 
'All done!';
}

Вы можете отловить вызов класса Event с помощью метода PHPshouldReceive() этого фасада, который вернёт экземпляр объекта-заглушки Mockery.

Заглушка фасада

PHP
public function testGetIndex()
{
  
Event::shouldReceive('fire')->once()->with('foo', ['name' => 'Dayle']);

  
$this->call('GET''/');
}

Не делайте этого для объекта Request. Вместо этого, передайте желаемый ввод методу PHPcall() во время выполнения вашего теста.

Вспомогательные методы

Класс TestCase содержит несколько вспомогательных методов для упрощения тестирования вашего приложения.

Установка и очистка сессий из теста

PHP
$this->session(['foo' => 'bar']);

$this->flushSession();

Установка текущего авторизованного пользователя

Вы можете установить текущего авторизованного пользователя с помощью метода PHPbe():

PHP
$user = new User(['name' => 'John']);

$this->be($user);

Заполнение БД тестовыми данными

Вы можете заполнить вашу БД начальными данными из теста методом PHPseed():

PHP
$this->seed();

$this->seed('DatabaseSeeder');

Больше информации на тему начальных данных доступно в разделе о миграциях.

Обновление приложения

Как вы уже возможно знаете, вы можете получить доступ к сервис-контейнеру вашего приложения с помощью PHP$this->app из любого тестового метода. Этот экземпляр сервис-контейнера обновляется для каждого тестового класса. Если вы хотите вручную обновить приложение для определённого метода, вы можете использовать метод PHPrefreshApplication() из этого тестового метода. Это приведет к сбросу дополнительных привязок, таких как заглушки, которые были помещены в сервис-контейнер после запуска теста.

Написать комментарий

Разметка: ? ?

Авторизуйся, чтобы прокомментировать.