troccoli's avatar

Mocking Socialite Facade

I use Socialite to allow login from Microsoft, and I am trying to test the process.

Since I don't want to call Microsoft I am trying to mock Socialite.

I have both Pest and Cypress tests, so I put the following in the AppServiceProvider.php. Note that Socialite is already correctly extended in the boot() method.

public function register(): void
{
    if ($this->app->environment('testing')) {
        $mockUser = Mockery::mock(
            MicrosoftUser::class,
            function (MockInterface $mock) {
                $mock->shouldReceive('getEmail')->andReturn('[email protected]');
                $mock->shouldReceive('getName')->andReturn('Test Microsoft User');
            },
        )->makePartial();

        $mockMicrosoft = Mockery::mock(
            MicrosoftProvider::class,
            function (MockInterface $mock) use ($mockUser) {
                $mock->shouldReceive('user')->andReturn($mockUser);
            },
        )->makePartial();

        Socialite::shouldReceive('driver')
            ->with('microsoft')
            ->andReturn($mockMicrosoft);
    }
}

However, I get the following error

[2024-12-03 16:38:35] testing.ERROR: Target [Laravel\Socialite\Contracts\Factory] is not instantiable. {"exception":"[object] (Illuminate\\Contracts\\Container\\BindingResolutionException(code: 0): Target [Laravel\\Socialite\\Contracts\\Factory] is not instantiable. at /Users/giuliotroccoli-allard/Herd/app-php-webapp/vendor/laravel/framework/src/Illuminate/Container/Container.php:1195)

Can anybody help?

0 likes
7 replies
LaryAI's avatar
Level 58

The error you're encountering, "Target [Laravel\Socialite\Contracts\Factory] is not instantiable," typically occurs when the Laravel service container is unable to resolve a dependency. This can happen if the binding for the Socialite facade is not properly set up in the testing environment.

To mock the Socialite facade correctly, you need to ensure that the Socialite service provider is registered and that the facade is properly configured. Here's a step-by-step solution to address the issue:

  1. Ensure Socialite Service Provider is Registered: Make sure that the SocialiteServiceProvider is registered in your config/app.php under the providers array. This is usually done automatically when you install Socialite, but it's good to double-check.

  2. Mocking in Tests: Instead of placing the mocking logic in the AppServiceProvider, it's often better to handle mocking within your test cases. This ensures that the mocking is only applied when running tests and doesn't affect other parts of your application.

  3. Mocking Socialite in a Test: Here's how you can mock the Socialite facade in a test case using Pest or PHPUnit:

  4. Ensure Mockery is Closed: If you're using Mockery, ensure that you close it after your tests to avoid memory leaks. You can do this by adding the tearDown method in your test class:

    protected function tearDown(): void
    {
        Mockery::close();
        parent::tearDown();
    }
    

By following these steps, you should be able to mock the Socialite facade effectively in your tests without encountering the "not instantiable" error.

martinbean's avatar

@troccoli When I’ve tested Socialite routes in the past, I‘ve done it like this:

troccoli's avatar

Thanks @martinbean

However, your suggestions, which I knew already, would only work for Pest tests (or PHPUnit). I'm looking for a solution which would work for Cypress tests too, hence why I've tried the service provider.

troccoli's avatar

I also get a few errors about translator

Target class [translator] does not exist

Which make me think I really cannot mock anything in a service provider. At least not that way....

troccoli's avatar
troccoli
OP
Best Answer
Level 6

That was a big clue. The issue was that I used the register() method instead of the boot() method everything worked (after a few modifications)

public function boot(): void
{
    if (! $this->app->environment('testing')) {
        return;
    }

    $mockUser = Mockery::mock(MicrosoftUser::class);
    $mockUser->shouldReceive('getEmail')->andReturn('[email protected]');
    $mockUser->shouldReceive('getName')->andReturn('Test Microsoft User');

    $mockProvider = Mockery::mock(
        MicrosoftProvider::class,
        [
            $this->app->make(Request::class),
            'microsoft-client-id',
            'microsoft-client-secret',
            'callback',
        ],
    )->makePartial();
    $mockProvider->shouldAllowMockingProtectedMethods();
    $mockProvider->shouldReceive('user')->andReturn($mockUser);
    $mockProvider->shouldReceive('getAuthUrl')->passthru();

    Socialite::partialMock()
        ->shouldReceive('driver')
        ->with('microsoft')
        ->andReturn($mockProvider);
}

Please or to participate in this conversation.