How to check out Laravel Socialite

I have an application using socialite, I want to create a test for Github authentication. So I used the Socialite Facade to make fun of the call to the Socialite driver method, but when I run my test, it tells me that I'm trying to get a value for the null type.

Below is the test I wrote

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

Below is a test implementation

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

I understand why it can return such a result, because Sociallite::driver($provider) returns an instance of Laravel\Socialite\Two\GithubProvider , and given that I cannot create this value, it will not be possible to specify the return type. I need help to successfully test the controller. Thanks

+6
source share
3 answers

Well, both answers were great, but they have many codes that are not required, and I was able to deduce my answer from them.

That is all I need to do.

Mock Socialite User Type First

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

Second, set the expected values โ€‹โ€‹for its method calls

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

Third, you need to make fun of the provider / user call

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

Then finally you write your statements

 $this->visit('/auth/google/callback') ->seePageIs('/') 
+4
source
 $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('/'); 

Note: use the socialite package at the top of your class

use Laravel \ Socialite \ Facades \ Socialite;

I had the same problem, but I was able to solve it using the above technique; @ceejayoz. Hope this helps.

+5
source

It may be harder to do, but I find it makes more readable tests. Hope you can help me simplify what Iโ€™m going to describe.

My idea is to drown out http requests. Considering facebook, there are two of them: 1) /oauth/access_token (to get the access token), 2) /me (to get user data).

To do this, I temporarily bound php to mitmproxy to create a vcr fixture:

  • Tell php use http proxy (add the following lines to the .env file):

     HTTP_PROXY=http://localhost:8080 HTTPS_PROXY=http://localhost:8080 
  • Tell php where the proxy certificate is: add openssl.cafile = /etc/php/mitmproxy-ca-cert.pem in php.ini . Or curl.cainfo , for that matter.

  • Reboot php-fpm .
  • Start mitmproxy .
  • Make your browser connected via mitmproxy .
  • Log in to the site you are developing using facebook (there is no TDD here).

    Press z in mitmproxy ( C for mitmproxy <0.18) to clear the request (stream) list before redirecting to facebook, if necessary. Or, alternatively, use the f command ( l for mitmproxy <0.18) with graph.facebook.com to filter additional requests.

    Note that for twitter you will need league/oauth1-client 1.7 or later. One switched from guzzle/guzzle to guzzlehttp/guzzle . Or you wonโ€™t be able to log in.

  • Copy the data from mimtproxy to tests/fixtures/facebook . I used the yaml format and here is what it looks like:

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

    You can use the E command for this if you have mitmproxy > = 0.18. Alternatively, use the P command. It copies the request / response to the clipboard. If you want mitmproxy retain their right to a file, you can run it with DISPLAY= mitmproxy .

    I do not see the possibility of using php-vcr , as I am not testing the entire workflow.

With this, I was able to write the following tests (and yes, they are fine with all of these values โ€‹โ€‹replaced by dots, feel free to copy as is).

Please note , but the sizes depend on the version of laravel/socialite . I had a problem with facebook. In version 2.0.16 laravel/socialite started making post messages to get an access token. Also there is api version in facebook urls.

These lights are for 2.0.14 . One way to handle this is to have the laravel/socialite dependency in the require-dev section of the composer.json file (with strict version specifications) to ensure that socialite has the correct version in the development environment (hopefully composer will ignore the section in require-dev in a production environment.) Given that you are running composer install --no-dev in a production environment.

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

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

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

Note. Make sure you have php-vcr/phpunit-testlistener-vcr , and that you have the following line in phpunit.xml :

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

There was also a problem with $_SERVER['HTTP_HOST'] , which was not installed during the tests. I am talking about the config/services.php file here, namely the URL redirection. I processed it like this:

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

Not particularly beautiful, but I could not find a better way. I was going to use config('app.url') there, but it does not work in configuration files.

UPD You can get rid of the setUpBeforeClass part by deleting this method, running tests, and updating some of the inventory requests with which vcr records. Actually, all this can be done only with vcr (no mitmproxy ).

+4
source

All Articles