Я пытаюсь использовать unit test мои контроллеры в Laravel 5, но у меня серьезные проблемы, обворачивающие его. Похоже, мне приходится торговать большими функциями коротких рук и статическими классами для эквивалентных инъекций эквивалентов, если я действительно хочу выполнить изолированное тестирование.
Во-первых, то, что я вижу в документации как "Unit testing" , не является модульным тестированием для меня. Это похоже на функциональное тестирование. Я не могу протестировать изолированную функцию контроллера, так как мне придется пройти через всю инфраструктуру, и мне нужно будет засеять мою базу данных, если у меня есть код, взаимодействующий с моей базой данных.
Итак, в свою очередь, я хочу протестировать мои контроллеры, изолированные от фреймворка. Это, однако, довольно сложно.
Посмотрим на эту функцию примера (я не использовал некоторые части этой функции для вопроса):
public function postLogin(\Illuminate\Http\Request $request)
{
$this->validate($request, [
'email' => 'required|email', 'password' => 'required',
]);
$credentials = $request->only('email', 'password');
if (Auth::attempt($credentials, $request->has('remember')))
{
return redirect()->intended($this->redirectPath());
}
}
Теперь проблема возникает в заключительных строках. Конечно, я могу издеваться над экземпляром Request, который отправляется функции, и что нет проблем. Но как я буду высмеивать класс Auth или функцию перенаправления? Мне нужно переписать свой класс/функцию с помощью инъекции зависимостей следующим образом:
private $auth;
private $redirector;
public function __construct(Guard $auth, \Illuminate\Routing\Redirector $redirector)
{
$this->auth = $auth;
$this->redirector = $redirector;
}
public function postLogin(\Illuminate\Http\Request $request)
{
$this->validate($request, [
'email' => 'required|email', 'password' => 'required',
]);
$credentials = $request->only('email', 'password');
if ($this->auth->attempt($credentials, $request->has('remember')))
{
return $this->redirector->intended($this->redirectPath());
}
}
И я заканчиваю запутанным unit test, полным mocks:
public function testPostLoginWithCorrectCredentials()
{
$guardMock = \Mockery::mock('\Illuminate\Contracts\Auth\Guard', function($mock){
$mock->shouldReceive('attempt')->with(['email' => 'test', 'password' => 'test'], false)->andReturn(true);
});
$redirectorMock = \Mockery::mock('\Illuminate\Routing\Redirector', function($mock){
$mock->shouldReceive('intended')->andReturn('/somePath');
});
$requestMock = \Mockery::mock('\Illuminate\Http\Request', function($mock){
$mock->shouldReceive('only')->with('email', 'password')->andReturn(['email' => 'test', 'password' => 'test']);
$mock->shouldReceive('has')->with('remember')->andReturn(false);
});
$object = new AuthController($guardMock, $redirectorMock);
$this->assertEquals('/somePath', $object->postLogin($requestMock));
}
Теперь, если бы у меня была более сложная логика, которая, например, использовала бы модель, я бы тоже должен был добавить эту зависимость и издеваться над ней в моем классе.
Мне кажется, что Laravel не обеспечивает то, что я хочу, или моя логика тестирования ошибочна. Могу ли я проверить свои функции контроллера, не выполняя функции тестирования вне контроля и/или имеющую возможность вводить стандартные классы Laravel, доступные мне в любом случае в моем контроллере?