Laravel Unit Testing Dependency Injection

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company
# Mastering Dependency Injection in Laravel Unit Tests: Resolving Container Errors As senior developers working with the Laravel ecosystem, we frequently deal with the power of Dependency Injection (DI). It’s what makes Laravel so flexible, allowing us to decouple components and manage complexity through the Service Container. However, when we move from live application requests into the isolated world of unit testing, this mechanism can sometimes introduce unexpected hurdles, particularly around dependency resolution. The issue you are encountering—where a class resolves fine in a controller but fails during a PHPUnit test using `resolve()`—is a classic symptom of the difference between a live request lifecycle and a test bootstrap lifecycle within the Laravel Service Container. Let's dive into why this happens and how we can correctly manage dependencies when testing services like your `ShoppingCart`. ## The Root Cause: Contextual Binding Mismatch The error you see, `Unresolvable dependency resolving`, indicates that when the container attempts to resolve `App\Classes\Billing\ShoppingCart` inside your test's constructor, it cannot find a valid binding for it within the current testing context. In a live request, Laravel's Service Providers and configuration files are loaded in a specific order, ensuring all necessary bindings (e.g., route definitions, service class mappings) are present. In a standard PHPUnit setup, especially when running tests directly against classes without fully booting the application environment, the container might lack the necessary context or service provider registrations that would normally make your custom class resolvable by default. This isn't necessarily an error in your `ShoppingCart` class itself; it’s an error in how the testing environment is initialized relative to the Service Container definitions. To fix this, we need to explicitly tell the container what to expect during tests. ## Practical Solutions for Testing Dependencies There are several robust ways to handle dependency injection in your tests, moving beyond simple `resolve()` calls when dealing with complex services. ### 1. Mocking Dependencies (The Isolation Approach) For pure unit testing—where you want to test the logic within `ShoppingCartTest` without worrying about the actual implementation of the service—the best practice is often to mock the dependency entirely. This ensures your test focuses only on the class under test, adhering to the principles of isolation that underpin modern application design, as promoted by frameworks like those found at [laravelcompany.com](https://laravelcompany.com). Instead of resolving the real service, inject a mock object: ```php use Illuminate\Support\Facades\Mockery; class ShoppingCartTest extends TestCase { protected $mockShoppingCart; protected function setUp(): void { parent::setUp(); // Create a mock to stand in for the actual ShoppingCart $this->mockShoppingCart = Mockery::mock('App\Classes\Billing\ShoppingCart'); } public function test_product_addition() { // Set up expectations on the mock if needed $this->mockShoppingCart->shouldReceive('addProduct')->once(); // Inject the mock instead of resolving the container $this->assertTrue(true); // Or assert based on mock interactions } } ``` ### 2. Binding in Test Service Providers (The Framework Approach) If you absolutely need to test that the *actual* service is correctly wired up, you should utilize Laravel's testing tools. You can define specific bindings or mocks within a dedicated Test Service Provider. This method keeps your tests clean while leveraging the container’s power when necessary. For scenarios requiring complex setup, consider using Laravel Factories and Service Container bindings to create lightweight, test-specific instances of your services. This aligns perfectly with how Laravel manages service resolution across the application lifecycle. ### 3. Explicit Manual Resolution (The Fallback) If you must use `resolve()`, ensure that any necessary Service Providers are loaded before the test runs. While this often involves manipulating the bootstrap process, for simple cases, ensuring your base `TestCase` properly extends Laravel's testing scaffolding is usually sufficient. If the class truly isn't bound at all, manual instantiation (if possible) or mocking remains the safer path to avoid container resolution exceptions. ## Conclusion Dependency Injection is a powerful tool, but mastering it in testing requires understanding the context of the Service Container. When unit testing classes that depend on complex services, resist the temptation to rely solely on `resolve()` if you encounter binding errors. Instead, embrace mocking and explicit dependency supply. This ensures your tests remain fast, isolated, and accurately reflect the behavior of your application logic, making your codebase more robust and easier to maintain.