Laravel PHPUnit mock Request
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
Title: Laravel PHPUnit Mock Request for Controllers: A Comprehensive Guide
Body:
As a senior developer working on a Laravel application, you may encounter situations where you need to test your controller logic using PHPUnit tests. Among the various aspects to consider is correctly mocking and controlling the Request object in your PHPUnit tests. In this blog post, we'll explore how to achieve this effectively while ensuring accurate results.
Testing Controller Logic with Mock Requests
To begin, let's first review a basic Laravel controller and an associated test case involving the Request object:use Illuminate\Http\Request;
public function insert(Request $request)
{
// ... some codes here
if ($request->has('username')) {
$userEmail = $request->get('username');
} else if ($request->has('email')) {
$userEmail = $request->get('email');
}
// ... some codes here
}
public function testIndex()
{
// ... some codes here
$requestParams = [
'username' => 'test',
'email' => 'test@test.com'
];
$request = $this->getMockBuilder('Illuminate\Http\Request')
->disableOriginalConstructor()
->setMethods(['getMethod', 'retrieveItem', 'getRealMethod', 'all', 'getInputSource', 'get', 'has'])
->getMock();
$request->expects($this->any())
->method('get')
->willReturn($requestParams);
$request->expects($this->any())
->method('has')
->willReturn($requestParams);
$request->expects($this->any())
->method('all')
->willReturn($requestParams);
// ... some codes here
}
Understanding the Issue with Mock Requests and the Expected Outcome
The problem lies in how PHPUnit handles Request objects within controller tests. The code shown earlier correctly mocks a Request object, but it doesn't work as intended. When executing this test case, thehas('username') method call always returns true because the provided mock request has the username key present in its array.
Similarly, if we remove the 'username' key from $requestParams and re-run the test, the request will still be considered valid for having a 'username', as it contains an empty array with that key. This isn't ideal because we want to verify that the Request object has a specific value or key under certain conditions in our tests.
Implementing Correct Mock Requests
To achieve the intended behavior, we need to update the PHPUnit test code for `insert()` as follows:public function testIndex()
{
// ... some codes here
$requestParams = [
'username' => 'test',
'email' => 'test@test.com'
];
$requestParamsWithoutUsername = array_diff($requestParams, ['username']);
$request = $this->getMockBuilder('Illuminate\Http\Request')
->disableOriginalConstructor()
->setMethods(['getMethod', 'retrieveItem', 'getRealMethod', 'all', 'getInputSource', 'get', 'has'])
->getMock();
$request->expects($this->any())
->method('get')
->willReturn(isset($requestParamsWithoutUsername['username']) ? $requestParamsWithoutUsername['username'] : null);
$request->expects($this->any())
->method('has')
->willReturn(function ($key) use ($requestParams, $requestParamsWithoutUsername) {
return isset($requestParams[$key]) || (isset($requestParamsWithoutUsername[$key]) && !isset($requestParams[$key]));
});
// ... some codes here
}
This test case now accurately handles the Request object in our controller logic. When calling `has('username')`, it will return true if the username key is present in $requestParams, and false if it's in $requestParamsWithoutUsername (without having a 'username' key explicitly). This ensures that we are testing the correct behavior for various scenarios and can make assertions based on the presence of specific keys or values within our Request object.