donglv9116 2014-04-11 02:31
浏览 72

PHPSpec和Laravel - 如何处理未找到双重方法的问题

I appear to be having issues with my spec tests when it comes to stubs that are calling other methods.

I've been following Laracasts 'hexagonal' approach for my controller to ensure it is only responsible for the HTTP layer.

Controller

<?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('/');
    }


}

So when the route is initialised I construct a new Connect instance and I pass an instance of $this class to my Connect class (to act as a listener) and call the loginOrCreate method.

Apes\Utilities\Connect

<?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

I've tried to trim this down to focus on the methods under test and my understanding thus far as to what is going wrong.

Connect Spec

<?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);
    }

So here's the spec that I'm having issues with. First I pretend that I've got a facebook token already. Then, where things are failing, is that I need to fudge that the getFacebookUserData method will return a sample user that exists in my users table.

However when I run the test I get:

Apes/Utilities/Connect                               
  37  ! it should login the user
      method `Double\Artdarek\OAuth\Facade\OAuth\P13::requestAccessToken()` not found.

I had hoped that 'willReturn' would just ignore whatever was happening in the getFacebookUserData method as I'm testing that separately, but it seems not.

Any recommendations on what I should be doing?

Do I need to pull all of the OAuth class methods into their own class or something? It seems strange to me that I might need to do that considering OAuth is already its own class. Is there some way to stub the method in getFacebookUserData?

Update 1

So I tried stubbing the method that's being called inside getFacebookUserData and my updated spec looks like this:

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);
}

The spec still fails but the error has changed:

Apes/Utilities/Connect                               
  37  ! it should login the user
      method `Double\Artdarek\OAuth\Facade\OAuth\P13::requestAccessToken()` is not defined.

Interestingly if I place these new stubs after the $this->getFacebookUserData stub then the error is 'not found' instead of 'not defined'. Clearly I don't fully understand the inner workings at hand :D

  • 写回答

2条回答 默认 最新

  • dongzhong8834 2014-04-11 02:49
    关注

    Not everything, called methods in your dependencies have to be mocked, because they will in fact be called while testing your classes:

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

    If you don't mock them, phpspec will raise a not found.

    评论

报告相同问题?

悬赏问题

  • ¥20 win11修改中文用户名路径
  • ¥15 win2012磁盘空间不足,c盘正常,d盘无法写入
  • ¥15 用土力学知识进行土坡稳定性分析与挡土墙设计
  • ¥70 PlayWright在Java上连接CDP关联本地Chrome启动失败,貌似是Windows端口转发问题
  • ¥15 帮我写一个c++工程
  • ¥30 Eclipse官网打不开,官网首页进不去,显示无法访问此页面,求解决方法
  • ¥15 关于smbclient 库的使用
  • ¥15 微信小程序协议怎么写
  • ¥15 c语言怎么用printf(“\b \b”)与getch()实现黑框里写入与删除?
  • ¥20 怎么用dlib库的算法识别小麦病虫害