How I write integration tests for Laravel Socialite powered apps

Published: December 29th, 2016

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.

<?php

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 corrseponding 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 redirect to the correct url. Here's the test do to 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.

Questions?

Have a question about this post or anything else?