Похоже, у меня возникают проблемы с моими тестами спецификаций, когда дело доходит до заглушек, которые вызывают другие методы.
Я следовал «шестиугольному» подходу Laracasts для своего контроллера, чтобы убедиться, что он отвечает только за уровень HTTP.
Контроллер
<?php
use Apes\Utilities\Connect;
use \OAuth;
class FacebookConnectController extends \BaseController {
/**
* @var $connect
*/
protected $connect;
/**
* Instantiates $connect
*
* @param $connect
*/
function __construct()
{
$this->connect = new Connect($this, OAuth::consumer('Facebook'));
}
/**
* Login user with facebook
*
* @return void
*/
public function initialise() {
// TODO: Actually probably not needed as we'll control
// whether this controller is called via a filter or similar
if(Auth::user()) return Redirect::to('/');
return $this->connect->loginOrCreate(Input::all());
}
/**
* User authenticated, return to main game view
* @return Response
*/
public function facebookConnectSucceeds()
{
return Redirect::to('/');
}
}
Итак, когда маршрут инициализирован, я создаю новый экземпляр Connect и передаю экземпляр класса $this в свой класс Connect (чтобы действовать как слушатель) и вызываю метод loginOrCreate.
Обезьяны\Утилиты\Подключение
<?php
namespace Apes\Utilities;
use Apes\Creators\Account;
use Illuminate\Database\Eloquent\Model;
use \User;
use \Auth;
use \Carbon\Carbon as Carbon;
class Connect
{
/**
* @var $facebookConnect
*/
protected $facebookConnect;
/**
* @var $account
*/
protected $account;
/**
* @var $facebookAuthorizationUri
*/
// protected $facebookAuthorizationUri;
/**
* @var $listener
*/
protected $listener;
public function __construct($listener, $facebookConnect)
{
$this->listener = $listener;
$this->facebookConnect = $facebookConnect;
$this->account = new Account();
}
public function loginOrCreate($input)
{
// Not the focus of this test
if(!isset($input['code'])){
return $this->handleOtherRequests($input);
}
// Trying to stub this method is my main issue
$facebookUserData = $this->getFacebookUserData($input['code']);
$user = User::where('email', '=', $facebookUserData->email)->first();
if(!$user){
// Not the focus of this test
$user = $this->createAccount($facebookUserData);
}
Auth::login($user, true);
// I want to test that this method is called
return $this->listener->facebookConnectSucceeds();
}
public function getFacebookUserData($code)
{
// I can't seem to stub this method because it's making another method call
$token = $this->facebookConnect->requestAccessToken($code);
return (object) json_decode($this->facebookConnect->request( '/me' ), true);
}
// Various other methods not relevant to this question
Я попытался урезать это, чтобы сосредоточиться на тестируемых методах и моем понимании того, что идет не так.
Подключить спецификацию
<?php
namespace spec\Apes\Utilities;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use \Illuminate\Routing\Controllers\Controller;
use \OAuth;
use \Apes\Creators\Account;
class ConnectSpec extends ObjectBehavior
{
function let(\FacebookConnectController $listener, \OAuth $facebookConnect, \Apes\Creators\Account $account)
{
$this->beConstructedWith($listener, $facebookConnect, $account);
}
function it_should_login_the_user($listener)
{
$input = ['code' => 'afacebooktoken'];
$returnCurrentUser = (object) [
'email' => '[email protected]',
];
$this->getFacebookUserData($input)->willReturn($returnCurrentUser);
$listener->facebookConnectSucceeds()->shouldBeCalled();
$this->loginOrCreate($input);
}
Итак, вот спецификация, с которой у меня проблемы. Сначала я притворяюсь, что у меня уже есть токен facebook. Затем, где что-то не получается, мне нужно обмануть то, что метод getFacebookUserData вернет образец пользователя, который существует в моей таблице пользователей.
Однако, когда я запускаю тест, я получаю:
Apes/Utilities/Connect
37 ! it should login the user
method `Double\Artdarek\OAuth\Facade\OAuth\P13::requestAccessToken()` not found.
Я надеялся, что willReturn просто проигнорирует все, что происходит в методе getFacebookUserData, поскольку я тестирую это отдельно, но, похоже, это не так.
Любые рекомендации о том, что я должен делать?
Мне нужно вытащить все методы класса OAuth в свой собственный класс или что-то в этом роде? Мне кажется странным, что мне может понадобиться это сделать, учитывая, что OAuth уже является отдельным классом. Есть ли способ заглушить метод в getFacebookUserData?
Обновление 1
Итак, я попытался заглушить метод, который вызывается внутри getFacebookUserData, и моя обновленная спецификация выглядит так:
function it_should_login_the_user($listener, $facebookConnect)
{
$returnCurrentUser = (object) [
'email' => '[email protected]',
];
$input = ['code' => 'afacebooktoken'];
// Try stubbing any methods that are called in getFacebookUserData
$facebookConnect->requestAccessToken($input)->willReturn('alongstring');
$facebookConnect->request($input)->willReturn($returnCurrentUser);
$this->getFacebookUserData($input)->willReturn($returnCurrentUser);
$listener->facebookConnectSucceeds()->shouldBeCalled();
$this->loginOrCreate($input);
}
Спецификация по-прежнему не работает, но ошибка изменилась:
Apes/Utilities/Connect
37 ! it should login the user
method `Double\Artdarek\OAuth\Facade\OAuth\P13::requestAccessToken()` is not defined.
Интересно, что если я размещу эти новые заглушки после заглушки $this->getFacebookUserData, тогда ошибка будет «не найдена», а не «не определена». Очевидно, я не до конца понимаю внутреннюю работу :D