2016-02-09 12 views
6

Mam aplikację, która korzysta z socialitu, chcę stworzyć test do uwierzytelniania Github, więc użyłem Faceci do wyśmiewania połączenia z metodą Socialite driver, ale kiedy uruchomię mój test, to mówi mi że próbuję uzyskać wartość na typie null.Jak testować Laravel Socialite

Poniżej jest test Pisałem

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

Poniżej jest implementacja testu

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

rozumiem dlaczego to może powrócić taki rezultat, ponieważ Sociallite::driver($provider) zwraca instancją Laravel\Socialite\Two\GithubProvider, i biorąc pod uwagę że nie jestem w stanie utworzyć tej wartości, niemożliwe będzie określenie typu zwrotu. Potrzebuję pomocy, aby pomyślnie przetestować kontroler. Dzięki

+0

Myślę, że możesz chcieć 'Socialite :: shouldReceive ('driver-> redirect')'. – ceejayoz

+0

@ceejayoz To nie działa, narzeka, że ​​nie widzi metody "driver-> redirect' –

Odpowiedz

4

Cóż, obie odpowiedzi były świetne, ale mają wiele kodów, które nie są wymagane, i mogłem wywnioskować z nich moją odpowiedź.

To wszystko, co musiałem zrobić.

pierwsze mock towarzystwa Typ użytkownika

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

Po drugie, należy ustawić wartości oczekiwane dla jego metody nazywa

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

Po trzecie, trzeba mock wywołanie operator/użytkownik

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

Następnie na koniec piszesz swoje twierdzenia:

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

wygląda na dużo brakujących tutaj –

+0

Nie dużo, tylko maniak kierowcy, którego zapomniałem dodać. –

+0

Nie rozumiem tej części 'Socialite :: shouldReceive ('driver-> user') -> andReturn ($ abstractValidUser);", czegoś brakuje? część 'driver-> user' ma być dokładnie – 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('/'); 

Uwaga: use pakiet towarzystwa na szczycie swojej klasy

użycie laravel \ ekonomista \ elewacje \ towarzystwa;

Miałem ten sam problem, ale udało mi się go rozwiązać za pomocą powyższej techniki; @ceejayoz. Mam nadzieję, że to pomoże.

4

To może być trudniejsze do zrobienia, ale uważam, że powoduje to bardziej czytelne testy. Mam nadzieję, że pomożesz mi uprościć to, co zamierzam opisać.

Mój pomysł jest taki, że żąda http. Biorąc pod uwagę facebook, są dwa z nich: 1) /oauth/access_token (aby uzyskać token dostępu), 2) /me (aby uzyskać dane o użytkowniku).

Do tego ja chwilowo załączony php do mitmproxy stworzyć vcr urządzenie:

  1. Powiadom php używać proxy HTTP (dodaj poniższe linie do pliku .env):

    HTTP_PROXY=http://localhost:8080 
    HTTPS_PROXY=http://localhost:8080 
    
  2. Powiadom php gdzie certyfikat proxy to: dodaj openssl.cafile = /etc/php/mitmproxy-ca-cert.pem do php.ini. Lub o numer curl.cainfo.

  3. Ponownie uruchom php-fpm.
  4. Uruchom mitmproxy.
  5. Uczyń też, że twoja przeglądarka łączy się przez mitmproxy.
  6. Zaloguj się do strony, którą tworzysz za pomocą Facebooka (tutaj nie ma TDD).

    Prasa z w mitmproxy (C dla mitmproxy < 0,18), aby usunąć żądanie (przepływu) wykaz przed przekierowaniem do facebook, jeśli zajdzie taka potrzeba. Lub użyj polecenia f (l dla mitmproxy < 0.18), aby odfiltrować dodatkowe żądania.

    Pamiętaj, że na twitter będziesz potrzebować league/oauth1-client w wersji 1.7 lub nowszej. Ta zmieniona z guzzle/guzzle na guzzlehttp/guzzle. W przeciwnym razie nie będziesz mógł się zalogować.

  7. Skopiuj dane z mimtproxy do tests/fixtures/facebook. Kiedyś formatu yaml i oto jak to wygląda:

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

    za to można użyć polecenia E jeśli masz mitmproxy> = 0,18. Alternatywnie użyj polecenia P. Kopiuje żądanie/odpowiedź do schowka. Jeśli chcesz, aby mitmproxy zapisać je bezpośrednio do pliku, możesz uruchomić go z DISPLAY= mitmproxy.

    Nie widzę możliwości korzystania z urządzeń rejestrujących php-vcr, ponieważ nie testuję całego przepływu pracy.

Dzięki temu mogłem napisać następujące testy (i tak, są one w porządku, gdy wszystkie te wartości są zastępowane kropkami, można kopiować tak, jak jest).

Pamiętaj jednak, że urządzenia są uzależnione od wersji laravel/socialite. Miałem problem z Facebookiem. W wersji 2.0.16laravel/socialite zaczęto wykonywać post requests, aby uzyskać token dostępu. W adresach URL Facebooka jest też api version.

Te oprawy są dla 2.0.14. Jednym ze sposobów radzenia sobie z tym problemem jest posiadanie również zależności laravel/socialite w sekcji require-dev pliku composer.json (ze ścisłą specyfikacją wersji), aby upewnić się, że socialite jest odpowiedniej wersji w środowisku programistycznym (mam nadzieję, że composer zignoruje tę w sekcji require-dev w środowisku produkcyjnym .) Biorąc pod uwagę, że wykonujesz composer install --no-dev w środowisku produkcyjnym.

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:

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

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

Uwaga. Upewnij się, że masz php-vcr/phpunit-testlistener-vcr wymagane, i że masz następującą linię w twojej phpunit.xml:

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

Był też problem z $_SERVER['HTTP_HOST'] nie są ustawione, po uruchomieniu testów.Mówię tu o pliku config/services.php, a mianowicie o przekierowaniu adresu URL. Tak sobie radziłem:

<?php 

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

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

Niezbyt piękna, ale nie udało mi się znaleźć lepszego sposobu. Zamierzałem użyć tam config('app.url'), ale nie działa to w plikach konfiguracyjnych.

UPD Można pozbyć setUpBeforeClass części poprzez usunięcie tej metody uruchamiania testów i uaktualniania żądania część opraw z zapisów co magnetowidu. W rzeczywistości wszystko można zrobić samemu (vcr) (nr).

+0

, jeśli cokolwiek, to jest naprawdę ciekawy pomysł używając php-vcr. –