Possible to mock model method output in Laravel / PHPUnit?
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
Possible to Mock Model Method Output in Laravel / PHPUnit? Unlocking Control Over Eloquent Logic
As developers working within the Laravel ecosystem, testing business logic is paramount. We rely on tools like PHPUnit and mocking libraries like Mockery to ensure that our application behaves exactly as expected under all conditions. A common hurdle arises when dealing with Eloquent Models: how do we control the output of a method defined directly on the model class, especially when that method contains complex external dependencies or failure points?
This post delves into whether you can effectively mock or fake the output of a method within an Eloquent Model in Laravel testing, and explores the most robust architectural patterns to achieve this.
The Challenge with Eloquent Models
The scenario you presented—mocking Website::checkDomainConfiguration() to force it to return true or false during a controller execution—is highly realistic. When a model method executes real logic (like calling dns_get_record), testing becomes brittle because external factors (network status, DNS resolution) dictate the result, not the application logic itself.
The difficulty often stems from how Eloquent models are instantiated and managed by Laravel’s Service Container. Simply mocking a method on an object doesn't automatically override the behavior when that object is resolved through the framework layers. While techniques like using app()->instance() with mocks (as shown in your example) can work, they often introduce tight coupling and make tests fragile if the dependency chain changes later.
Strategy 1: Direct Method Mocking with Mockery
It is technically possible to mock methods on Eloquent models using tools like Mockery. This technique is useful for isolating specific behaviors, but it requires careful handling of how Laravel resolves dependencies.
For your example, where you need to control the return value of checkDomainConfiguration, the approach involves mocking the model instance itself and ensuring that when the controller calls the method, the mock provides the desired result.
Here is a conceptual breakdown of how Mockery can be applied:
use Mockery;
use App\Models\Website; // Assuming your model namespace
// Setup the mock website object
$websiteMock = Mockery::mock(Website::class);
// Define the expected behavior for the method we want to control
$websiteMock->shouldReceive('checkDomainConfiguration')
->with($domain->domain) // Ensure it receives the correct argument
->once()
->andReturn(true); // Force the method to return true
// Crucially, you need to ensure that when resolving this object in the controller context,
// Laravel sees the mock. This often involves binding or instance replacement:
app()->instance(Website::class, $websiteMock);
// Now, execute the controller logic...
While this achieves the goal of forcing a return value, developers should be aware that relying heavily on app()->instance() inside tests can complicate setup and maintenance. It is generally considered better practice to test interactions rather than deep internal state manipulation when possible, especially with Eloquent models, as promoted by Laravel best practices found on laravelcompany.com.
Strategy 2: The Superior Approach – Decoupling Logic into Services
For complex business logic residing within your Models—especially logic that interacts with external systems (like DNS lookups) or involves intricate validation—the most robust pattern is to decouple that logic out of the Eloquent model and place it into dedicated Service Classes. This adheres to the Single Responsibility Principle (SRP).
Instead of putting checkDomainConfiguration directly on the Website model, you create a WebsiteService.
Model: Becomes a data container.
Service: Becomes the logic executor.
// app/Services/WebsiteService.php
class WebsiteService
{
public function checkDomainConfiguration(string $domain): bool
{
try {
$records = dns_get_record($domain, DNS_A);
} catch (\ErrorException $e) {
return false;
}
if (isset($records[0]['ip']) && $records[0]['ip'] === getenv('SERVER_IPV4_ADDRESS')) {
return true;
}
return false;
}
}
Controller: Now resolves the service, which is much easier to mock:
// In your controller constructor or via route binding
public function __construct(private Website $website, WebsiteService $websiteService) {}
public function store(Website $website, Domain $domain)
{
// We test the Service directly in the unit test, not the model's internal state.
if (! $this->websiteService->checkDomainConfiguration($domain->domain)) {
return response(['error' => 'Domain configuration failed'], 500);
}
// ... continue logic
}
In your PHPUnit test, you can now easily mock the WebsiteService to return any value you need without manipulating the application container:
$websiteServiceMock = Mockery::mock(WebsiteService::class);
$websiteServiceMock->shouldReceive('checkDomainConfiguration')
->with($domain->domain)
->once()
->andReturn(true);
// Inject the mock service into the controller or test the service directly.
Conclusion
While it is technically possible to force Eloquent Model methods to return specific values using mocking techniques, this approach often leads to testing that is tightly coupled to the framework's internal state and can be fragile.
As a senior developer, I strongly recommend adopting Strategy 2: Decoupling Logic into Service Classes. This pattern allows you to write clean, highly testable code where your tests focus purely on the business rules (the service layer) rather than manipulating the intricacies of the ORM framework. By following this principle, you ensure that your application remains flexible, maintainable, and adheres to the principles of robust Laravel development.