How to mock Laravel Eloquent model with phpunit?

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company
# How to Mock Laravel Eloquent Models with PHPUnit: Navigating the ORM Challenge Testing services that rely heavily on Eloquent models often presents a unique challenge. Unlike plain PHP classes, Laravel Eloquent models are not just simple data containers; they are sophisticated objects deeply integrated with the database layer, featuring accessors, mutators, relationships, and hydration logic. When you try to mock an Eloquent model directly in a unit test, you often run into unexpected behavior, leading to properties appearing as `null` even after manual assignment. This post will dive deep into why standard mocking techniques fail for Eloquent models and present the most practical, robust strategies for testing your business logic by mocking model data effectively. ## The Pitfall of Mocking Eloquent Objects Directly You’ve encountered a very common hurdle: attempting to set properties on a mock instance of an Eloquent model. Let’s look at why your attempts—using `setAttribute()` or directly assigning properties after creating a mock—result in `null`. Eloquent models inherit complex behavior. When you use tools like PHPUnit's mocking, you are typically mocking the class structure itself. While you can set public properties on a generic mock object, Eloquent’s internal mechanisms (especially those related to mutators and lazy loading) often interfere with simple property assignments, making the mocked object behave inconsistently or incorrectly when tested against service layers. The core issue is that unit testing should focus on testing *your logic*, not the intricate hydration mechanism of the ORM. If your service layer only needs data (like an `id` and a `name`), you shouldn't need to mock every single Eloquent method; you just need to provide the necessary input data. ## Recommended Strategy: Mocking Data, Not the Model Structure Instead of trying to force PHPUnit to perfectly mimic an entire hydrated Eloquent object, the best practice in unit testing is often to use mocks for the *data source* or *repository* that supplies the model, and then inject simple, plain data into your service. This keeps your tests focused on the business rules you are validating, rather than the ORM's implementation details. ### Method 1: Mocking a Repository or Factory (The Clean Approach) If your service layer depends on fetching a `User`, mock the mechanism that fetches it, not the model itself. If you are using Laravel Repositories (a common pattern suggested by modern Laravel development), mocking the repository is far cleaner. For example, if your service calls a repository to get a user: ```php use App\Services\UserRepository; use Illuminate\Support\Facades\DB; class UserServiceTest extends TestCase { public function test_user_can_be_processed() { // 1. Create a mock repository $mockRepository = $this->createMock(UserRepository::class); // 2. Define the data the repository should return when asked for a user $mockRepository->method('find')->willReturn(new User(['id' => 23, 'name' => 'Test User'])); // 3. Inject the mock into your service $service = new UserService($mockRepository); // 4. Test the business logic $result = $service->processUser(23); // Or whatever method you designed // Assertions on $result... } } ``` This approach tests that your `UserService` correctly handles the data it receives, completely bypassing the need to wrestle with mocking Eloquent's internal structure. This aligns perfectly with the principles of dependency injection and testing advocated by the Laravel community. ### Method 2: Stubbing Properties for Simple Models (When Necessary) If you absolutely must mock the model itself—perhaps because you are testing a method directly on the model that doesn't involve external dependencies—you should still rely on setting properties directly, but ensure your test setup handles potential exceptions or errors gracefully. For simple read-only properties like `id`, direct assignment *can* work if you bypass Eloquent’s full hydration layer during the mock setup: ```php use App\Models\User; public function test_direct_mocking() { // Create a mock instance of the Eloquent class $mockUser = $this->getMockBuilder(User::class) ->disableOriginalConstructor() ->getMock(); // Set the property directly. For simple attributes, this often works // in controlled unit tests where you are testing attribute access. $mockUser->id = 23; $mockUser->name = 'Mocked Name'; $this->assertEquals(23, $mockUser->id); } ``` **A Note on Eloquent and Testing:** Remember that when dealing with ORM entities like those found in the Laravel ecosystem, focus your unit tests on what *your service* does with the data. Relying too heavily on mocking the ORM object itself can lead to brittle tests that break when Laravel updates its internal structure. For deep integration testing involving database interactions, use feature tests or dedicated database fixtures instead of trying to trick PHPUnit into perfectly simulating Eloquent’s magic. ## Conclusion Mocking Laravel Eloquent models requires a shift in perspective. Don't try to mock the complex hydration layer; instead, mock the data flow that feeds your service. By focusing on mocking repositories and injecting plain data into your services, you write tests that are decoupled, maintainable, and truly test the business logic of your application, ensuring a solid foundation for any project built on Laravel principles.