如何测试Laravel社交名流

Jam*_*rge 9 php testing laravel mockery

我有一个使用社交网站的应用程序,我想为Github身份验证创建测试,所以我使用Socialite Facade来模拟对Socialite driver方法的调用,但是当我运行我的测试时它告诉我我正在尝试获取null值类型.

以下是我写的测试

public function testGithubLogin()
{
    Socialite::shouldReceive('driver')
        ->with('github')
        ->once();
    $this->call('GET', '/github/authorize')->isRedirection();
}
Run Code Online (Sandbox Code Playgroud)

以下是测试的实现

public function authorizeProvider($provider)
{
    return Socialite::driver($provider)->redirect();
}
Run Code Online (Sandbox Code Playgroud)

我理解为什么它会返回这样的结果因为Sociallite::driver($provider)返回一个实例Laravel\Socialite\Two\GithubProvider,并且考虑到我无法实例化这个值,就不可能指定一个返回类型.我需要帮助来成功测试控制器.谢谢

Olo*_*ope 10

$provider = Mockery::mock('Laravel\Socialite\Contracts\Provider');
$provider->shouldReceive('redirect')->andReturn('Redirected');
$providerName = class_basename($provider);
//Call your model factory here
$socialAccount = factory('LearnCast\User')->create(['provider' => $providerName]);

$abstractUser = Mockery::mock('Laravel\Socialite\Two\User');
// Get the api user object here
$abstractUser->shouldReceive('getId') 
             ->andReturn($socialAccount->provider_user_id)
             ->shouldReceive('getEmail')
             ->andReturn(str_random(10).'@noemail.app')
             ->shouldReceive('getNickname')
             ->andReturn('Laztopaz')
             ->shouldReceive('getAvatar')
             ->andReturn('https://en.gravatar.com/userimage');

$provider = Mockery::mock('Laravel\Socialite\Contracts\Provider');
$provider->shouldReceive('user')->andReturn($abstractUser);

Socialite::shouldReceive('driver')->with('facebook')->andReturn($provider);

// After Oauth redirect back to the route
$this->visit('/auth/facebook/callback')
// See the page that the user login into
->seePageIs('/');
Run Code Online (Sandbox Code Playgroud)

注意:use您的班级顶部的社交名媛包

使用Laravel\Socialite\Facades\Socialite;

我有同样的问题,但我能够使用上述技术解决它; @ceejayoz.我希望这有帮助.


Jam*_*rge 9

嗯,这两个答案都很棒,但是它们有很多不需要的代码,我能够从中推断出我的答案.

这就是我需要做的.

首先模拟Socialite用户类型

$abstractUser = Mockery::mock('Laravel\Socialite\Two\User')
Run Code Online (Sandbox Code Playgroud)

其次,设置方法调用的期望值

$abstractUser
   ->shouldReceive('getId')
   ->andReturn(rand())
   ->shouldReceive('getName')
   ->andReturn(str_random(10))
   ->shouldReceive('getEmail')
   ->andReturn(str_random(10) . '@gmail.com')
   ->shouldReceive('getAvatar')
   ->andReturn('https://en.gravatar.com/userimage');
Run Code Online (Sandbox Code Playgroud)

第三,您需要模拟提供者/用户调用

Socialite::shouldReceive('driver->user')->andReturn($abstractUser);
Run Code Online (Sandbox Code Playgroud)

最后你写下你的断言

$this->visit('/auth/google/callback')
     ->seePageIs('/')
Run Code Online (Sandbox Code Playgroud)

  • 我不明白这部分`Socialite::shouldReceive('driver->user')->andReturn($abstractValidUser);`,有什么遗漏吗?`driver->user` 部分完全正确 (2认同)

x-y*_*uri 5

这可能更难做到,但我相信这可以提高测试的可读性。希望您能帮助我简化我将要描述的内容。

我的想法是存根http请求。考虑到 facebook,有两个:1)/oauth/access_token(获取访问令牌),2)/me(获取有关用户的数据)。

为此,我暂时附加phpmitmproxy创建vcr固定装置:

  1. 告诉php使用 http 代理(将以下行添加到文件中.env):

    HTTP_PROXY=http://localhost:8080
    HTTPS_PROXY=http://localhost:8080
    
    Run Code Online (Sandbox Code Playgroud)
  2. 告诉php代理的证书在哪里:添加openssl.cafile = /etc/php/mitmproxy-ca-cert.pemphp.ini. 或者curl.cainfo,就此而言。

  3. 重新开始php-fpm
  4. 开始mitmproxy
  5. mitmproxy让您的浏览器也能连接。
  6. 使用 facebook 登录到您正在开发的网站(这里没有 TDD)。

    如果需要,请在重定向到 Facebook 之前按 (< 0.18) 清除请求(z流)mitmproxy列表Cmitmproxy或者,使用f命令(l< mitmproxy0.18)来graph.facebook.com过滤掉额外的请求。

    请注意,对于 Twitter,您需要league/oauth1-client1.7 或更高版本。那个从 切换guzzle/guzzleguzzlehttp/guzzle。否则你将无法登录。

  7. 将数据从 复制mimtproxytests/fixtures/facebook。我使用了yaml格式,如下所示:

    -
        request:
            method: GET
            url: https://graph.facebook.com/oauth/access_token?client_id=...&client_secret=...&code=...&redirect_uri=...
        response:
            status:
                http_version: '1.1'
                code: 200
                message: OK
            body: access_token=...&expires=...
    -
        request:
            method: GET
            url: https://graph.facebook.com/v2.5/me?access_token=...&appsecret_proof=...&fields=first_name,last_name,email,gender,verified
        response:
            status:
                http_version: '1.1'
                code: 200
                message: OK
            body: '{"first_name":"...","last_name":"...","email":"...","gender":"...","verified":true,"id":"..."}'
    
    Run Code Online (Sandbox Code Playgroud)

    为此,E如果您的值mitmproxy>= 0.18,则可以使用命令。或者,使用命令P。它将请求/响应复制到剪贴板。如果您想mitmproxy将它们保存到文件中,您可以使用DISPLAY= mitmproxy.

    我看不出如何使用 的php-vcr录音设施,因为我没有测试整个工作流程。

这样我就能够编写以下测试(是的,它们可以将所有这些值替换为点,请随意按原样复制)。

但请注意,固定装置取决于 的laravel/socialite版本。我在使用 Facebook 时遇到了问题。在版本中2.0.16 laravel/socialite开始执行发布请求以获取访问令牌。Facebook 网址中也有api 版本

这些装置适用于2.0.14. 处理它的一种方法是laravel/socialite在文件require-dev部分composer.json中也具有依赖关系(具有严格的版本规范),以确保socialite开发环境中的版本正确(希望composer能够忽略require-dev生产环境中的部分)。考虑到您composer install --no-dev在生产环境。

AuthController_HandleFacebookCallbackTest.php

<?php

use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Auth;
use VCR\VCR;

use App\User;

class AuthController_HandleFacebookCallbackTest extends TestCase
{
    use DatabaseTransactions;

    static function setUpBeforeClass()
    {
        VCR::configure()->enableLibraryHooks(['stream_wrapper', 'curl'])
            ->enableRequestMatchers([
                'method',
                'url',
            ]);
    }

    /**
     * @vcr facebook
     */
    function testCreatesUserWithCorrespondingName()
    {
        $this->doCallbackRequest();

        $this->assertEquals('John Doe', User::first()->name);
    }

    /**
     * @vcr facebook
     */
    function testCreatesUserWithCorrespondingEmail()
    {
        $this->doCallbackRequest();

        $this->assertEquals('john.doe@gmail.com', User::first()->email);
    }

    /**
     * @vcr facebook
     */
    function testCreatesUserWithCorrespondingFbId()
    {
        $this->doCallbackRequest();

        $this->assertEquals(123, User::first()->fb_id);
    }

    /**
     * @vcr facebook
     */
    function testCreatesUserWithFbData()
    {
        $this->doCallbackRequest();

        $this->assertNotEquals('', User::first()->fb_data);
    }

    /**
     * @vcr facebook
     */
    function testRedirectsToHomePage()
    {
        $this->doCallbackRequest();

        $this->assertRedirectedTo('/');
    }

    /**
     * @vcr facebook
     */
    function testAuthenticatesUser()
    {
        $this->doCallbackRequest();

        $this->assertEquals(User::first()->id, Auth::user()->id);
    }

    /**
     * @vcr facebook
     */
    function testDoesntCreateUserIfAlreadyExists()
    {
        $user = factory(User::class)->create([
            'fb_id' => 123,
        ]);

        $this->doCallbackRequest();

        $this->assertEquals(1, User::count());
    }

    function doCallbackRequest()
    {
        return $this->withSession([
            'state' => '...',
        ])->get('/auth/facebook/callback?' . http_build_query([
            'state' => '...',
        ]));
    }
}
Run Code Online (Sandbox Code Playgroud)

tests/fixtures/facebook

-
    request:
        method: GET
        url: https://graph.facebook.com/oauth/access_token
    response:
        status:
            http_version: '1.1'
            code: 200
            message: OK
        body: access_token=...
-
    request:
        method: GET
        url: https://graph.facebook.com/v2.5/me
    response:
        status:
            http_version: '1.1'
            code: 200
            message: OK
        body: '{"first_name":"John","last_name":"Doe","email":"john.doe\u0040gmail.com","id":"123"}'
Run Code Online (Sandbox Code Playgroud)

AuthController_HandleTwitterCallbackTest.php

<?php

use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Auth;
use VCR\VCR;
use League\OAuth1\Client\Credentials\TemporaryCredentials;

use App\User;

class AuthController_HandleTwitterCallbackTest extends TestCase
{
    use DatabaseTransactions;

    static function setUpBeforeClass()
    {
        VCR::configure()->enableLibraryHooks(['stream_wrapper', 'curl'])
            ->enableRequestMatchers([
                'method',
                'url',
            ]);
    }

    /**
     * @vcr twitter
     */
    function testCreatesUserWithCorrespondingName()
    {
        $this->doCallbackRequest();

        $this->assertEquals('joe', User::first()->name);
    }

    /**
     * @vcr twitter
     */
    function testCreatesUserWithCorrespondingTwId()
    {
        $this->doCallbackRequest();

        $this->assertEquals(123, User::first()->tw_id);
    }

    /**
     * @vcr twitter
     */
    function testCreatesUserWithTwData()
    {
        $this->doCallbackRequest();

        $this->assertNotEquals('', User::first()->tw_data);
    }

    /**
     * @vcr twitter
     */
    function testRedirectsToHomePage()
    {
        $this->doCallbackRequest();

        $this->assertRedirectedTo('/');
    }

    /**
     * @vcr twitter
     */
    function testAuthenticatesUser()
    {
        $this->doCallbackRequest();

        $this->assertEquals(User::first()->id, Auth::user()->id);
    }

    /**
     * @vcr twitter
     */
    function testDoesntCreateUserIfAlreadyExists()
    {
        $user = factory(User::class)->create([
            'tw_id' => 123,
        ]);

        $this->doCallbackRequest();

        $this->assertEquals(1, User::count());
    }

    function doCallbackRequest()
    {
        $temporaryCredentials = new TemporaryCredentials();
        $temporaryCredentials->setIdentifier('...');
        $temporaryCredentials->setSecret('...');
        return $this->withSession([
            'oauth.temp' => $temporaryCredentials,
        ])->get('/auth/twitter/callback?' . http_build_query([
            'oauth_token' => '...',
            'oauth_verifier' => '...',
        ]));
    }
}
Run Code Online (Sandbox Code Playgroud)

tests/fixtures/twitter

-
    request:
        method: POST
        url: https://api.twitter.com/oauth/access_token
    response:
        status:
            http_version: '1.1'
            code: 200
            message: OK
        body: oauth_token=...&oauth_token_secret=...
-
    request:
        method: GET
        url: https://api.twitter.com/1.1/account/verify_credentials.json
    response:
        status:
            http_version: '1.1'
            code: 200
            message: OK
        body: '{"id_str":"123","name":"joe","screen_name":"joe","location":"","description":"","profile_image_url":"http:\/\/pbs.twimg.com\/profile_images\/456\/userpic.png"}'
Run Code Online (Sandbox Code Playgroud)

AuthController_HandleGoogleCallbackTest.php

<?php

use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Auth;
use VCR\VCR;

use App\User;

class AuthController_HandleGoogleCallbackTest extends TestCase
{
    use DatabaseTransactions;

    static function setUpBeforeClass()
    {
        VCR::configure()->enableLibraryHooks(['stream_wrapper', 'curl'])
            ->enableRequestMatchers([
                'method',
                'url',
            ]);
    }

    /**
     * @vcr google
     */
    function testCreatesUserWithCorrespondingName()
    {
        $this->doCallbackRequest();

        $this->assertEquals('John Doe', User::first()->name);
    }

    /**
     * @vcr google
     */
    function testCreatesUserWithCorrespondingEmail()
    {
        $this->doCallbackRequest();

        $this->assertEquals('john.doe@gmail.com', User::first()->email);
    }

    /**
     * @vcr google
     */
    function testCreatesUserWithCorrespondingGpId()
    {
        $this->doCallbackRequest();

        $this->assertEquals(123, User::first()->gp_id);
    }

    /**
     * @vcr google
     */
    function testCreatesUserWithGpData()
    {
        $this->doCallbackRequest();

        $this->assertNotEquals('', User::first()->gp_data);
    }

    /**
     * @vcr google
     */
    function testRedirectsToHomePage()
    {
        $this->doCallbackRequest();

        $this->assertRedirectedTo('/');
    }

    /**
     * @vcr google
     */
    function testAuthenticatesUser()
    {
        $this->doCallbackRequest();

        $this->assertEquals(User::first()->id, Auth::user()->id);
    }

    /**
     * @vcr google
     */
    function testDoesntCreateUserIfAlreadyExists()
    {
        $user = factory(User::class)->create([
            'gp_id' => 123,
        ]);

        $this->doCallbackRequest();

        $this->assertEquals(1, User::count());
    }

    function doCallbackRequest()
    {
        return $this->withSession([
            'state' => '...',
        ])->get('/auth/google/callback?' . http_build_query([
            'state' => '...',
        ]));
    }
}
Run Code Online (Sandbox Code Playgroud)

tests/fixtures/google

-
    request:
        method: POST
        url: https://accounts.google.com/o/oauth2/token
    response:
        status:
            http_version: '1.1'
            code: 200
            message: OK
        body: access_token=...
-
    request:
        method: GET
        url: https://www.googleapis.com/plus/v1/people/me
    response:
        status:
            http_version: '1.1'
            code: 200
            message: OK
        body: '{"emails":[{"value":"john.doe@gmail.com"}],"id":"123","displayName":"John Doe","image":{"url":"https://googleusercontent.com/photo.jpg"}}'
Run Code Online (Sandbox Code Playgroud)

笔记。确保您已php-vcr/phpunit-testlistener-vcr要求,并且您的 中包含以下行phpunit.xml

<listeners>
    <listener class="PHPUnit_Util_Log_VCR" file="vendor/php-vcr/phpunit-testlistener-vcr/PHPUnit/Util/Log/VCR.php"/>
</listeners>
Run Code Online (Sandbox Code Playgroud)

$_SERVER['HTTP_HOST']运行测试时还存在未设置的问题。我这里说的是config/services.php文件,即重定向url。我是这样处理的:

 <?php

$app = include dirname(__FILE__) . '/app.php';

return [
    ...
    'facebook' => [
        ...
        'redirect' => (isset($_SERVER['HTTP_HOST']) ? 'http://' . $_SERVER['HTTP_HOST'] : $app['url']) . '/auth/facebook/callback',
    ],
];
Run Code Online (Sandbox Code Playgroud)

不是特别漂亮,但我没能找到更好的方法。我本来打算在那里使用config('app.url'),但它在配置文件中不起作用。

UPDsetUpBeforeClass您可以通过删除此方法、运行测试并使用 vcr 记录更新装置的请求部分来摆脱部分。实际上,整个事情可以单独完成vcr(否mitmproxy)。