Some information might be out of date, as I don't always update older articles.

I recently began working on screeenly again and wanted to share a neat trick how you could write integration tests for your Laravel Socialite integration.

Example Application Code#

Let's assume you have successfully installed laravel/socialite in your project and everything is set up to work with the GitHub OAuth API. You would have the following controller somewhere in your application.

namespace App \ Http \ Controllers \ App \ OAuth ; use App \ Http \ Controllers \ Controller ; use App \ Models \ User ; use Laravel \ Socialite \ Contracts \ Factory as Socialite ; class GithubController extends Controller { protected $socialite; protected $user; public function __construct (Socialite $socialite, User $user) { $this ->socialite = $socialite; $this ->user = $user; } public function redirect () { return $this ->socialite->driver( 'github' )->scopes([ 'user:email' ])->redirect(); } public function handle () { $oAuthUser = $this ->socialite->driver( 'github' )->user(); if (! $this ->user->exists($oAuthUser) { $user = $this ->user->create($oAuthUser); } else { $user = $this ->user->getByOAuthInformation($oAuthUser); } auth()->login($user); return redirect()->route( 'home' ); } }

The corresponding routes would be oauth/github/redirect and oauth/github/handle .

In my test I added the following method to mock Socialite:

use Laravel \ Socialite \ Contracts \ Factory as Socialite ; public function mockSocialiteFacade ($email = 'foo</span>@<span class="hljs-string">bar.com' , $token = 'foo' , $id = 1 ) { $socialiteUser = $this ->createMock(Laravel\Socialite\Two\User::class); $socialiteUser->token = $token; $socialiteUser->id = $id; $socialiteUser->email = $email; $provider = $this ->createMock(Laravel\Socialite\Two\GithubProvider::class); $provider->expects( $this ->any()) ->method( 'user' ) ->willReturn($socialiteUser); $stub = $this ->createMock(Socialite::class); $stub->expects( $this ->any()) ->method( 'driver' ) ->willReturn($provider); $this ->app->instance(Socialite::class, $stub); }

First we create a mock of Laravel\Socialite\Two\User . This is the user object Socialite returns for OAuth2 calls and you will get when you execute the following code in your handle method in your controller:

$user = $this ->socialite->driver( 'github' )->user();

(I also added three arguments to the method which will then be attached to the mocked user object. This makes testing different scenarios much easier.)

Next, we create a mock of Laravel\Socialite\Two\GithubProvider . This instance will return our mocked User when we call the method user on it.

We also create a mock of Socialite which is an alias for the contract Laravel\Socialite\Contracts\Factory . This mock will return our mocked GithubProvider when the method driver is called. (Do you see how all these 3 mocks stick together?)

Next, we swap the Socialite implementation in our application with our mock. We use Laravel's Binding Feature for this.

Writing Tests#

Let's get started with our tests. First, we want to test that our users get redirected to the correct URL. Here's the test to do that:

public function it_redirects_to_github () { $response = $this ->call( 'GET' , '/oauth/github/redirect' ); $this ->assertContains( 'github.com/login/oauth' , $response->getTargetUrl()); }

The $response variable is an instance of Symfony\Component\HttpFoundation\RedirectResponse and has a convenient getTargetUrl() method on it. (I don't test the entire URL. I just want to be sure the user gets redirected to "github.com" and not "foo.com")

Next, we need to test the response which we receive from GitHub: When a user returns from the GitHub OAuth screen we should read the user information, create a new user, log the user in and redirect to our home route.

The tests would look like this:

public function it_retrieves_github_request_and_creates_a_new_user () { $this ->mockSocialiteFacade( 'foo</span>@<span class="hljs-string">bar.com' ); $this ->visit( '/oauth/github/handle' ) ->seePageIs( '/home' ); $this ->seeInDatabase( 'users' , [ 'email' => 'foo</span>@<span class="hljs-string">bar.com' , ]); }

Pretty easy, right? I'm sure there would be better ways to do this. If you have a better solution to this problem let me know.