PHPSpec и Laravel - как справиться с проблемами двойного метода, не найденными

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

Я следовал «шестиугольному» подходу 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


person alexleonard    schedule 11.04.2014    source источник


Ответы (2)


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

...

$facebookConnect->requestAccessToken($input)->willReturn(<whatever it should return>);

$this->getFacebookUserData($input)->willReturn($returnCurrentUser);

...

Если вы не издеваетесь над ними, phpspec поднимет not found.

person Antonio Carlos Ribeiro    schedule 11.04.2014
comment
Привет, Антонио, спасибо за ответ, но теперь я получаю ошибку not defined. Я обновил вопрос, указав измененную спецификацию и полное сообщение об ошибке. Я также попытался заглушить (или я должен сказать, издеваться?) метод facebookConnect->request, который находится в getFacebookUserData на всякий случай, но та же ошибка возвращается независимо от того, включаю ли я эту строку или нет. - person alexleonard; 11.04.2014

Я не знаком с задействованными классами, но эта ошибка подразумевает, что нет метода Oauth:: requestAccessToken().

Пророчество не позволит вам заглушить несуществующие методы.

person Ciaran McNulty    schedule 19.04.2014