PHPSpec和Laravel-如何处理双重方法未发现问题


PHPSpec and Laravel - how to handle double method not found issues

当涉及到调用其他方法的存根时,我的规范测试似乎有问题。

我一直在为我的控制器遵循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' => 'existinguser@domain.tld',
        ];
        $this->getFacebookUserData($input)->willReturn($returnCurrentUser);
        $listener->facebookConnectSucceeds()->shouldBeCalled();
        $this->loginOrCreate($input);
    }

这是我遇到问题的规范。首先,我假装我已经有了一个脸书代币。然后,事情失败的地方是,我需要伪造getFacebookUserData方法将返回一个存在于我的users表中的示例用户。

然而,当我运行测试时,我得到:

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' => 'existinguser@domain.tld',
    ];
    $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

并不是所有依赖项中被调用的方法都必须被嘲笑,因为它们实际上会在测试类时被调用:

...
$facebookConnect->requestAccessToken($input)->willReturn(<whatever it should return>);
$this->getFacebookUserData($input)->willReturn($returnCurrentUser);
...

如果不嘲笑它们,phpspec将引发一个not found

我不熟悉所涉及的类,但这个错误意味着没有方法Oauth::requestAccessToken()。

预言不会让你截断不存在的方法。