How to mock a service (or a ServiceProvider) when running Feature tests in Laravel?
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
# How to Mock a Service (or a ServiceProvider) when running Feature Tests in Laravel
Testing complex applications built on dependency injection, like those using Laravel, often presents a unique challenge: how do you isolate the code under test from its external dependencies? When your application relies on Service Providers to bind services as singletons, mocking these components during feature tests requires understanding and manipulating the Laravel Service Container.
The scenario you describedâwhere controllers depend on services injected via Service Providersâis extremely common in larger Laravel applications. While this structure promotes good separation of concerns, it can make unit testing tricky if you are trying to test only the controller logic without executing the actual business logic within the service layer.
This post will walk you through the most effective, "Laravel way" to mock services and dependencies during feature testing, moving beyond simple mocking techniques to demonstrate how to control dependency resolution.
## The Challenge: Controlling Service Binding in Tests
When Laravel boots up for a test, it executes the Service Providers defined in your application. If your `App\Providers\Server\Users` explicitly binds `App\Services\Users` as a singleton, any controller requesting `UserService` will automatically receive that bound instance. To test your controller in isolation (e.g., testing its routing or response logic) without hitting the actual database calls within the service, you need to intercept this binding.
The confusion around using static calls like `App::bind()` stems from the fact that while traditional PHP might require explicit namespace resolution, Laravel provides specific mechanisms within the test environment to interact with the container directly.
## The Solution: Overriding Bindings via the Service Container
The core solution is to instruct the application's Service Container to use a mock implementation whenever a specific interface or concrete class is requested during the test execution. This allows you to inject your mock object directly into the controllerâs constructor, bypassing the real service logic entirely.
### Step 1: Define an Interface (Best Practice)
Before mocking, always abstract your services behind interfaces. This adheres to SOLID principles and makes mocking much cleaner.
```php
// app/Services/UserServiceInterface.php
namespace App\Services;
interface UserServiceInterface
{
public function getUsers(): array;
public function createUser(array $data): bool;
}
```
### Step 2: Create the Mock Service
Create a dummy class that implements your interface and contains predictable, mocked behavior for testing purposes.
```php
// tests/MockServices/MockUserService.php
namespace Tests\MockServices;
use App\Services\UserServiceInterface;
class MockUserService implements UserServiceInterface
{
public function getUsers(): array
{
// Return canned data instead of hitting the database
return [
['id' => 1, 'name' => 'Test User'],
['id' => 2, 'name' => 'Mock User']
];
}
public function createUser(array $data): bool
{
// Simulate a successful creation without actual database interaction
return true;
}
}
```
### Step 3: Bind the Mock in Your Test Setup
In your feature test class, you use the `$this->app` facade (which is available within testing contexts) to override the binding for the duration of the test. This tells Laravel: "Whenever anything asks for `UserServiceInterface`, give it this mock instead."
```php
use Tests\MockServices\MockUserService;
use App\Services\UserServiceInterface;
class UserControllerTest extends TestCase
{
/**
* Set up the mock service before each test runs.
*/
protected function setUp(): void
{
parent::setUp();
// Override the binding for the interface to inject our mock
$this->app->bind(UserServiceInterface::class, new MockUserService());
}
public function testGetUsersReturnsMockedData()
{
// Now, when the controller tries to resolve UserService, it gets MockUserService.
$response = $this->json('GET', '/v1/users');
$response->assertStatus(200);
// Assert against the data returned by the mock, not the real database
$response->assertJson(['ok' => true]);
}
}
```
## Why This Approach Works and Laravel Philosophy
This technique works because it directly manipulates the Service Containerâthe heart of how Laravel manages dependencies. When you bind an interface to a concrete class within a test scope, you are effectively telling the container to resolve that dependency using your mock object instead of the one registered by your original Service Provider. This keeps your feature tests fast, isolated, and deterministic.
As emphasized in Laravel documentation, understanding the Service Container is key to mastering framework development. Whether itâs binding Eloquent models or mocking external API calls, controlling the bindings allows you to write highly testable code. For deeper dives into dependency management within Laravel applications, always refer back to the official resources at [laravelcompany.com](https://laravelcompany.com).
## Conclusion
Mocking services in Laravel feature tests is less about finding a single magic function and more about understanding how the framework resolves dependencies. By leveraging `$this->app->bind()`, you gain precise control over the service resolution process, allowing you to substitute complex business logic with simple mocks. This practice ensures your tests focus purely on testing the intended flow of your controller without the overhead or side effects of actual service execution.