2016-02-09 14 views
6

Ich habe eine Anwendung, die Socialite verwendet, möchte ich Test für Github Authentifizierung erstellen, also habe ich Socialite Facade zu mokieren Aufruf an die Socialite driver Methode, aber wenn ich meinen Test laufen, sagt es mir dass ich versuche, Wert auf Null-Typ zu bekommen.Wie Laravel Socialite zu testen

Unten ist der Test, den ich

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

Unterhalb der Durchführung des Tests wird

public function authorizeProvider($provider) 
{ 
    return Socialite::driver($provider)->redirect(); 
} 

Ich verstehe, warum es ein solches Ergebnis geschrieben haben, weil Sociallite::driver($provider) gibt eine Instanz von Laravel\Socialite\Two\GithubProvider zurückkehren könnten, und unter Berücksichtigung Da ich diesen Wert nicht instanziieren kann, ist es unmöglich, einen Rückgabetyp anzugeben. Ich brauche Hilfe, um den Controller erfolgreich zu testen. Dank

+0

Ich denke, Sie könnten wollen 'Socialite :: sollteReceive ('Treiber-> Redirect')'. – ceejayoz

+0

@ceejayoz Das funktioniert nicht, es beschwert sich, dass es die Methode 'Treiber-> Redirect' nicht sieht –

Antwort

4

Nun, beide Antworten waren großartig, aber sie haben viele Codes, die nicht benötigt werden, und ich konnte meine Antwort von ihnen ableiten.

Das ist alles was ich tun musste.

verspotten Zum einen die Socialite Benutzertyp

$abstractUser = Mockery::mock('Laravel\Socialite\Two\User') 

Zweitens setzen die erwarteten Werte für seine Methode aufruft

$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'); 

Drittens müssen Sie den Provider/Benutzeraufruf

Socialite::shouldReceive('driver->user')->andReturn($abstractUser); 
verspotten

Dann schreiben Sie schließlich Ihre Aussagen

$this->visit('/auth/google/callback') 
    ->seePageIs('/') 
+0

sieht aus wie viel hier fehlt –

+0

Nicht viel, nur der Fahrer Mock habe ich vergessen hinzuzufügen. –

+0

Ich verstehe diesen Teil 'Socialite :: shouldReceive ('driver-> user') -> andReturn ($ abstractValidUser);', fehlt etwas? der 'Treiber-> Benutzer' Teil genau zu sein – LTroya

5
$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('/'); 

Hinweis: use die Persönlichkeit des öffentlichen Lebens-Paket an der Spitze der Klasse

Verwendung Laravel \ Socialite \ Fassaden \ Socialite;

Ich hatte das gleiche Problem, aber ich konnte es mit der oben genannten Technik lösen; @ceejayoz. Ich hoffe das hilft.

4

Dies ist vielleicht schwieriger zu tun, aber ich glaube, es macht für lesbarere Tests. Hoffentlich hilfst du mir dabei, das zu vereinfachen, was ich beschreiben werde.

Meine Idee ist, HTTP-Anfragen stub. Unter Berücksichtigung von Facebook gibt es zwei davon: 1) /oauth/access_token (um Zugriffstoken zu erhalten), 2) /me (um Daten über den Benutzer zu erhalten).

Dafür habe ich angebracht vorübergehend php-mitmproxyvcr Befestigung zu schaffen:

  1. php Sagen Sie HTTP-Proxy zu verwenden (fügen Sie die folgenden Zeilen in die .env-Datei):

    HTTP_PROXY=http://localhost:8080 
    HTTPS_PROXY=http://localhost:8080 
    
  2. Sagen php Das Proxy-Zertifikat lautet: Fügen Sie openssl.cafile = /etc/php/mitmproxy-ca-cert.pem zu php.ini hinzu. Oder curl.cainfo, für diese Angelegenheit.

  3. Neustart php-fpm.
  4. Start mitmproxy.
  5. Machen Sie Ihren Browser durch mitmproxy auch verbunden.
  6. Melden Sie sich mit Facebook bei der Website an, die Sie entwickeln (keine TDD hier).

    Press z in mitmproxy (C für mitmproxy < 0,18) Anfrage (flow) Liste zu löschen, bevor sein, wenn nötig auf facebook umleiten. Alternativ können Sie auch f (l für mitmproxy < 0.18) mit graph.facebook.com verwenden, um zusätzliche Anforderungen herauszufiltern.

    Beachten Sie, dass Sie für Twitter benötigen Sie league/oauth1-client 1.7 oder neuer. Der eine wechselte von zu guzzlehttp/guzzle. Sonst können Sie sich nicht anmelden.

  7. Kopieren Sie Daten von mimtproxy zu tests/fixtures/facebook. Ich benutzte yaml Format und hier ist, wie es aussieht:

    - 
        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":"..."}' 
    

    Dafür Sie E Befehl verwenden können, wenn Sie mitmproxy> = 0,18 haben. Verwenden Sie alternativ den Befehl P. Es kopiert die Anfrage/Antwort in die Zwischenablage. Wenn Sie mitmproxy möchten, um sie direkt in Datei zu speichern, können Sie es mit DISPLAY= mitmproxy ausführen.

    Ich sehe keine Möglichkeit php-vcr 's Aufnahmemöglichkeiten zu verwenden, da ich nicht den gesamten Arbeitsablauf teste.

Damit konnte ich die folgenden Tests (und ja, sie sind gut mit all den durch Punkte ersetzt Werte, fühlen sich frei, wie zu kopieren) schreiben.

Beachten Sie bitte, Leuchten hängen von der laravel/socialite Version ab. Ich hatte ein Problem mit Facebook. In Version 2.0.16laravel/socialite gestartet post requests, um Zugriffstoken zu erhalten. Auch gibt es api version in Facebook-URLs.

Diese Vorrichtungen sind für 2.0.14. Eine Möglichkeit, damit umzugehen, ist laravel/socialite Abhängigkeit in require-dev Abschnitt composer.json Datei auch (mit strenger Versionsspezifikation) zu gewährleisten, dass socialite der richtigen Version in der Entwicklungsumgebung ist (composer wird hoffentlich ignorieren die in require-dev Abschnitt in der Produktionsumgebung .) Unter Berücksichtigung Sie tun composer install --no-dev in der Produktionsumgebung.

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('[email protected]', 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' => '...', 
     ])); 
    } 
} 

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"}' 

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' => '...', 
     ])); 
    } 
} 

tests/fixtures/twitter:

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('[email protected]', 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' => '...', 
     ])); 
    } 
} 

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":"[email protected]"}],"id":"123","displayName":"John Doe","image":{"url":"https://googleusercontent.com/photo.jpg"}}' 

Hinweis. Vergewissern Sie sich, php-vcr/phpunit-testlistener-vcr erforderlich ist, und dass Sie die folgende Zeile in der phpunit.xml:

<listeners> 
    <listener class="PHPUnit_Util_Log_VCR" file="vendor/php-vcr/phpunit-testlistener-vcr/PHPUnit/Util/Log/VCR.php"/> 
</listeners> 

Es gab auch ein Problem mit $_SERVER['HTTP_HOST'] nicht gesetzt werden, wenn die Tests ausgeführt werden.Ich spreche über config/services.php Datei hier, nämlich über Redirect URL. Ich habe es so behandelt:

<?php 

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

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

Nicht besonders schön, aber ich habe es versäumt, einen besseren Weg zu finden. Ich würde dort config('app.url') verwenden, aber es funktioniert nicht in den Config-Dateien.

UPD Sie können setUpBeforeClass Teil durch Entfernen dieser Methode, Ausführen von Tests und Aktualisieren der Anfrage Teil der Fixtures mit was vcr Datensätze loswerden. Eigentlich könnte die ganze Sache mit vcr allein gemacht werden (keine mitmproxy).

+0

Wenn überhaupt, ist dies eine sehr interessante Idee mit php-vcr. –

Verwandte Themen