Документация Laravel 10.x
Здесь ты найдешь сниппеты по Laravel и полезные советы по веб-разработке.
При тестировании приложений Laravel вы можете "мокировать" определенные аспекты вашего приложения, чтобы они фактически не выполнялись во время определенного теста. Например, при тестировании контроллера, который отправляет событие, вы можете мокировать слушателей событий, чтобы они не выполнялись во время теста. Это позволяет вам тестировать только HTTP-ответ контроллера, не беспокоясь о выполнении слушателей событий, так как слушатели событий могут быть протестированы в их собственном тестовом случае.
Laravel предоставляет удобные методы для мокирования событий, задач и других фасадов "из коробки". Эти вспомогательные средства в основном предоставляют уровень удобства над Mockery, чтобы вам не приходилось вручную вызывать сложные методы Mockery.
При мокировании объекта, который будет внедрен в ваше приложение через сервисный контейнер Laravel, вам нужно будет привязать свой замок в контейнер как привязку instance
. Это указывает контейнеру использовать ваш замок объекта вместо создания объекта самостоятельно:
use App\Service;use Mockery;use Mockery\MockInterface; public function test_something_can_be_mocked(): void{ $this->instance( Service::class, Mockery::mock(Service::class, function (MockInterface $mock) { $mock->shouldReceive('process')->once(); }) );}
Для большего удобства вы можете использовать метод mock
, предоставленный базовым классом тестирования Laravel. Например, следующий пример эквивалентен приведенному выше примеру:
use App\Service;use Mockery\MockInterface; $mock = $this->mock(Service::class, function (MockInterface $mock) { $mock->shouldReceive('process')->once();});
Вы можете использовать метод partialMock
, когда вам нужно мокировать только несколько методов объекта. Методы, которые не мокируются, будут выполнены нормально при вызове:
use App\Service;use Mockery\MockInterface; $mock = $this->partialMock(Service::class, function (MockInterface $mock) { $mock->shouldReceive('process')->once();});
Аналогично, если вы хотите шпионить за объектом, базовый класс тестирования Laravel предлагает метод spy
как удобную оболочку вокруг метода Mockery::spy
. Шпионы подобны мокам; однако они записывают любое взаимодействие между шпионом и выполняемым кодом, позволяя вам делать утверждения после выполнения кода:
use App\Service; $spy = $this->spy(Service::class); // ... $spy->shouldHaveReceived('process');
В отличие от традиционных вызовов статических методов, фасады (включая фасады в реальном времени) могут быть замокированы. Это предоставляет большое преимущество перед традиционными статическими методами и предоставляет вам ту же тестируемость, которую вы получили бы, используя традиционную инъекцию зависимостей. При тестировании вы часто захотите замокировать вызов фасада Laravel, который происходит в одном из ваших контроллеров. Например, рассмотрим следующее действие контроллера:
<?php namespace App\Http\Controllers; use Illuminate\Support\Facades\Cache; class UserController extends Controller{ /** * Получить список всех пользователей приложения. */ public function index(): array { $value = Cache::get('key'); return [ // ... ]; }}
Мы можем замокировать вызов фасада Cache
, используя метод shouldReceive
, который вернет экземпляр Mockery mock. Поскольку фасады фактически разрешаются и управляются сервисным контейнером Laravel, они имеют гораздо большую тестируемость, чем типичный статический класс. Например, давайте замокируем вызов метода get
фасада Cache
:
<?php namespace Tests\Feature; use Illuminate\Support\Facades\Cache;use Tests\TestCase; class UserControllerTest extends TestCase{ public function test_get_index(): void { Cache::shouldReceive('get') ->once() ->with('key') ->andReturn('value'); $response = $this->get('/users'); // ... }}
Внимание Не следует мокировать фасад
Request
. Вместо этого передавайте ваши входные данные в методы тестирования HTTP, такие какget
иpost
, при запуске теста. Точно так же, вместо мокирования фасадаConfig
, вызывайте методConfig::set
ваших тестов.
Если вы хотите шпионить за фасадом, вы можете вызвать метод spy
на соответствующем фасаде. Шпионы подобны мокам; однако они записывают любое взаимодействие между шпионом и выполняемым кодом, позволяя вам делать утверждения после выполнения кода:
use Illuminate\Support\Facades\Cache; public function test_values_are_be_stored_in_cache(): void{ Cache::spy(); $response = $this->get('/'); $response->assertStatus(200); Cache::shouldHaveReceived('put')->once()->with('name', 'Taylor', 10);}
При тестировании иногда может потребоваться изменить время, возвращаемое вспомогательными методами, такими как now
или Illuminate\Support\Carbon::now()
. К счастью, базовый класс тестирования функций Laravel включает в себя вспомогательные методы, позволяющие вам манипулировать текущим временем:
use Illuminate\Support\Carbon; public function test_time_can_be_manipulated(): void{ // Путешествие в будущее... $this->travel(5)->milliseconds(); $this->travel(5)->seconds(); $this->travel(5)->minutes(); $this->travel(5)->hours(); $this->travel(5)->days(); $this->travel(5)->weeks(); $this->travel(5)->years(); // Путешествие в прошлое... $this->travel(-5)->hours(); // Путешествие в явное время... $this->travelTo(now()->subHours(6)); // Вернуться в настоящее время... $this->travelBack();}
Вы также можете предоставить замыкание для различных методов путешествия во времени. Замыкание будет вызвано с замороженным временем в указанный момент. После выполнения замыкания время возобновится как обычно:
$this->travel(5)->days(function () { // Проверить что-то пять дней вперед...}); $this->travelTo(now()->subDays(10), function () { // Проверить что-то в определенный момент...});
Метод freezeTime
можно использовать для замораживания текущего времени. Аналогично, метод freezeSecond
заморозит текущее время, но в начале текущей секунды:
use Illuminate\Support\Carbon; // Заморозить время и возобновить нормальный ход времени после выполнения замыкания...$this->freezeTime(function (Carbon $time) { // ...}); // Заморозить время на текущей секунде и возобновить нормальный ход времени после выполнения замыкания...$this->freezeSecond(function (Carbon $time) { // ...})
Как можно ожидать, все обсуждаемые выше методы в основном полезны для тестирования поведения приложения, зависящего от времени, например, блокировки неактивных постов на форуме для обсуждения:
use App\Models\Thread; public function test_forum_threads_lock_after_one_week_of_inactivity(){ $thread = Thread::factory()->create(); $this->travel(1)->week(); $this->assertTrue($thread->isLockedByInactivity());}