Jak otestovat Laravel Socialite

Úvod Přihlášení pomocí sociálních sítí je jedním z nejpohodlnějších způsobů, jak mohou uživatelé přistupovat k vaší aplikaci. Laravel to usnadňuje pomocí Laravel Socialite, balíče...

Poptat web

22. 07. 2025

Jak otestovat Laravel Socialite

Úvod

Přihlášení pomocí sociálních sítí je jedním z nejpohodlnějších způsobů, jak mohou uživatelé přistupovat k vaší aplikaci. Laravel to usnadňuje pomocí Laravel Socialite, balíček první strany, který abstrahuje od složitostí protokolu OAuth.

V tomto příspěvku si ukážeme skutečnou implementaci řadiče Socialite a hlavně, demystifikovat testy za ním. Testování ověřování třetí stranou se může zdát složité, ale s pomocí mockingu a chytrých tvrzení je naprosto proveditelné.

Společenský kontrolor: Přehled

Zde je aktuální SocialiteController třídy, kterou testujeme:

<?php
 
declare(strict_types=1);
 
namespace App\Http\Controllers\Auth;
 
use App\Models\User;
use Illuminate\Auth\Events\Verified;
use Illuminate\Database\UniqueConstraintViolationException;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Laravel\Socialite\Facades\Socialite;
 
class SocialiteController
{
// route: /auth/{provider}/redirect
public function redirect(string $provider): RedirectResponse
{
$this->validate(compact('provider'));
 
return Socialite::driver($provider)->redirect();
}
 
// route: /auth/{provider}/callback
public function handleCallback(string $provider): RedirectResponse
{
$this->validate(compact('provider'));
 
$user = Socialite::driver($provider)->user();
 
try {
$user = User::updateOrCreate([
'provider' => $provider,
'provider_id' => $user->id,
], [
'name' => $user->name ?? $user->nickname ?? 'N/A',
'email' => $user->email,
'password' => Str::random(),
'avatar_url' => $user->avatar,
]);
} catch (UniqueConstraintViolationException $exception) {
throw ValidationException::withMessages([
'email' => 'This email is already taken.',
]);
}
 
if (! $user->hasVerifiedEmail()) {
$user->markEmailAsVerified();
event(new Verified($user));
}
 
Auth::login($user, true);
 
return redirect()->intended(route('dashboard'));
}
 
protected function validate(array $data): void
{
Validator::make($data, [
'provider' => ['required', 'string', 'in:github,google'],
])->validate();
}
}

Tento ovladač:

  • Přesměruje uživatele na zprostředkovatele OAuth.
  • Zpracovává zpětné volání, aktualizuje nebo vytváří uživatele
  • V případě potřeby zpracovává ověření e-mailu
  • Přihlášení uživatele

Nyní se podívejme, jak tuto logiku důkladně otestovat.

Sada testů ověřování Laravel Socialite

Zde je celý testovací soubor s podrobným vysvětlením pro každý případ:

<?php
 
declare(strict_types=1);
 
use App\Models\User;
use Illuminate\Auth\Events\Verified;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Str;
use Laravel\Socialite\Contracts\Provider;
use Laravel\Socialite\Contracts\User as SocialiteUser;
use Laravel\Socialite\Facades\Socialite;
use Mockery\MockInterface;
 
// Test: Redirects to provider
 
test('redirects to auth provider', function () {
$this
->get(route('socialite.redirect', 'github'))
->assertValid()
->assertRedirect();
});
 
// Test: Invalid provider name fails validation
 
test('provider is invalid', function () {
$this
->get(route('socialite.redirect', Str::random()))
->assertInvalid(['provider']);
});
 
// Test: Handles callback and creates a new user
 
test('handles callback from auth provider and creates a user', function () {
$provider = Arr::random(['github', 'google']);
$user = User::factory()->unverified()->make();
 
Event::fake();
 
$socialiteUser = $this->mock(SocialiteUser::class, function (MockInterface $mock) use ($user) {
$mock->id = fake()->randomNumber(3);
$mock->name = $user->name;
$mock->email = $user->email;
$mock->avatar = $user->avatar_url;
});
 
$socialiteProvider = $this->mock(Provider::class, function (MockInterface $mock) use ($socialiteUser) {
$mock->shouldReceive('user')->andReturn($socialiteUser);
});
 
Socialite::shouldReceive('driver')->with($provider)->andReturn($socialiteProvider);
 
$this->assertDatabaseCount('users', 0);
 
$this
->get(route('socialite.callback', $provider))
->assertRedirect(route('dashboard'));
 
Event::assertDispatched(Verified::class);
 
$this
->assertAuthenticated()
->assertDatabaseCount('users', 1)
->assertDatabaseHas('users', [
'provider' => $provider,
'provider_id' => $socialiteUser->id,
'name' => $socialiteUser->name,
'email' => $socialiteUser->email,
'avatar_url' => $socialiteUser->avatar,
])
->assertTrue(User::firstWhere('email', $user->email)->hasVerifiedEmail());
});
 
// Test: Email already exists
 
test('user will not be created if already exists', function () {
$provider = Arr::random(['github', 'google']);
$user = User::factory()->create();
$this->assertDatabaseCount('users', 1);
 
$socialiteUser = $this->mock(SocialiteUser::class, function (MockInterface $mock) use ($user) {
$mock->id = fake()->randomNumber(3);
$mock->name = $user->name;
$mock->email = $user->email;
$mock->avatar = $user->avatar_url;
});
 
$socialiteProvider = $this->mock(Provider::class, function (MockInterface $mock) use ($socialiteUser) {
$mock->shouldReceive('user')->andReturn($socialiteUser);
});
 
Socialite::shouldReceive('driver')->with($provider)->andReturn($socialiteProvider);
 
$this
->get(route('socialite.callback', $provider))
->assertInvalid([
'email' => 'This email is already taken.',
]);
 
$this->assertDatabaseCount('users', 1);
});
 
// Test: Log in user if registered with the same provider
 
test('user will login and will not be created again if already registered with the same provider', function () {
$user = User::factory()->viaSocialite()->create();
$newName = fake()->name();
 
$socialiteUser = $this->mock(SocialiteUser::class, function (MockInterface $mock) use ($user, $newName) {
$mock->id = $user->provider_id;
$mock->name = $newName;
$mock->email = $user->email;
$mock->avatar = $user->avatar_url;
});
 
$socialiteProvider = $this->mock(Provider::class, function (MockInterface $mock) use ($socialiteUser) {
$mock->shouldReceive('user')->andReturn($socialiteUser);
});
 
Socialite::shouldReceive('driver')->with($user->provider)->andReturn($socialiteProvider);
 
$this->assertDatabaseCount('users', 1);
 
$this
->get(route('socialite.callback', $user->provider))
->assertRedirect(route('dashboard'));
 
$this
->assertAuthenticated()
->assertDatabaseCount('users', 1)
->assertDatabaseHas('users', [
'provider' => $user->provider,
'provider_id' => $socialiteUser->id,
'name' => $newName,
'email' => $socialiteUser->email,
'avatar_url' => $socialiteUser->avatar,
]);
});
 
// Test: Same email but different provider
 
test('user will not be created if already registered with another provider', function () {
$user = User::factory()->viaSocialite()->create();
$provider = collect(['google', 'github'])->diff([$user->provider])->random();
$this->assertDatabaseCount('users', 1);
 
$socialiteUser = $this->mock(SocialiteUser::class, function (MockInterface $mock) use ($user) {
$mock->id = fake()->randomNumber(3);
$mock->name = $user->name;
$mock->email = $user->email;
$mock->avatar = $user->avatar_url;
});
 
$socialiteProvider = $this->mock(Provider::class, function (MockInterface $mock) use ($socialiteUser) {
$mock->shouldReceive('user')->andReturn($socialiteUser);
});
 
Socialite::shouldReceive('driver')->with($provider)->andReturn($socialiteProvider);
 
$this
->get(route('socialite.callback', $provider))
->assertInvalid([
'email' => 'This email is already taken.',
]);
 
$this->assertDatabaseCount('users', 1);
});

Každý z těchto testů zahrnuje reálný scénář:

  • ✅ Šťastné cesty (úspěšné vytvoření, úspěšné přihlášení)
  • ❌ Cesty selhání (duplicitní e-mail z formuláře nebo neshodní poskytovatelé)
  • ✨ Vylepšení uživatelského prostředí (např. aktualizované informace o profilu)

Závěrečné myšlenky

Socialite usnadňuje přihlašování do sociálních sítí, ale bez řádného testování hrozí konflikty účtů, chybějící ověření a obtížně odstranitelné problémy s přihlašováním. Pomocí několika dobře vytvořených maket a tvrzení můžete simulovat celý tok a spolehlivě pokrýt okrajové případy.

Tipy pro testování Socialite:

  • Pomocí funkce Laravel mocking a fasád můžete simulovat služby třetích stran.
  • Ověřte pozitivní i negativní scénáře.
  • Nezapomeňte otestovat duplicitu e-mailů a neshody poskytovatelů.

Původní článek publikoval Nabil Hassen.