How I write Integration Tests for Laravel Socialite powered Apps
This article has been published a while ago.
If this is a technical article some information might be out of date. If something is terribly broken, let me know and I will update the article accordingly.
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;
    }
    /**
     * Redirect User to GitHub to approve OAuth Handshake.
     * @return Redirect
     */
    public function redirect()
    {
        return $this->socialite->driver('github')->scopes(['user:email'])->redirect();
    }
    /**
     * Handle Return Request from GitHub OAuth API
     * @return Redirect
     */
    public function handle()
    {
        $oAuthUser = $this->socialite->driver('github')->user();
        // Just Pseudo Code
        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.
Preparation #
In my test I added the following method to mock Socialite:
use Laravel\Socialite\Contracts\Factory as Socialite;
/**
 * Mock the Socialite Factory, so we can hijack the OAuth Request.
 * @param  string  $email
 * @param  string  $token
 * @param  int $id
 * @return void
 */
public function mockSocialiteFacade($email = '[email protected]', $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);
    // Replace Socialite Instance with our mock
    $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:
/** @test */
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:
/** @test */
public function it_retrieves_github_request_and_creates_a_new_user()
{
    // Mock the Facade and return a User Object with the email '[email protected]'
    $this->mockSocialiteFacade('[email protected]');
    $this->visit('/oauth/github/handle')
        ->seePageIs('/home');
    $this->seeInDatabase('users', [
        'email' => '[email protected]',
    ]);
}
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.