While working on an application I wanted to test my middleware. When doing some investigation on this issue I didn't find any satisfying solutions, most involved manually creating a Symfony request object or mocking the request entirely.
To aid in the testing of my middleware I created a set of helper methods and an assertion (you can also find them in this gist), which I have added to my TestCase
class:
<?php
namespace Tests;
use Illuminate\Foundation\Testing\TestResponse;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use PHPUnit\Framework\Assert;
abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
/**
* Setup the test environment.
*
* @return void
*/
protected function setUp()
{
parent::setUp();
TestResponse::macro('assertMiddlewarePassed', function () {
Assert::assertEquals('__passed__', $this->content());
});
}
/**
* Call the given middleware.
*
* @param string|string[] $middleware
* @param string $method
* @param array $data
* @return \Illuminate\Foundation\Testing\TestResponse
*/
protected function callMiddleware($middleware, $method = 'GET', array $data = [])
{
return $this->call(
$method, $this->makeMiddlewareRoute($method, $middleware), $data
);
}
/**
* Call the given middleware using a JSON request.
*
* @param string|string[] $middleware
* @param string $method
* @param array $data
* @return \Illuminate\Foundation\Testing\TestResponse
*/
protected function callMiddlewareJson($middleware, $method = 'GET', array $data = [])
{
return $this->json(
$method, $this->makeMiddlewareRoute($method, $middleware), $data
);
}
/**
* Make a dummy route with the given middleware applied.
*
* @param string $method
* @param string|string[] $middleware
* @return string
*/
protected function makeMiddlewareRoute($method, $middleware)
{
$method = strtolower($method);
return $this->app->make('router')->{$method}('/__middleware__', [
'middleware' => $middleware,
function () {
return '__passed__';
}
])->uri();
}
}
Example
To demonstrate the usage of these helpers, let's test a simple admin authentication middleware. The purpose of the middleware is to protect admin routes by only allowing admin users and redirecting away users and guests, or returning a forbidden response for JSON requests. The implementation is as follows:
<?php
namespace App\Http\Middleware;
class AdminAuthenticate
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, \Closure $next)
{
$user = $request->user();
if (is_null($user) || ! $user->isAdmin()) {
return $request->expectsJson()
? response()->json(['message' => 'Forbidden.'], 403)
: redirect()->home();
}
return $next($request);
}
}
Using the previously defined helper methods, we can now easily test this middleware class!
<?php
namespace Tests\Http\Middleware;
use Tests\Testcase;
use App\Http\Middleware\AdminAuthenticate;
use Illuminate\Foundation\Testing\RefreshDatabase;
class AdminAuthenticateTest extends TestCase
{
use RefreshDatabase;
/** @test */
public function it_passes_for_admins()
{
$user = factory(User::class)->create(['is_admin' => true]);
$response = $this->actingAs($user)->callMiddleware(AdminAuthenticate::class);
$response->assertMiddlewarePassed();
}
/** @test */
public function it_redirects_a_non_admin_user_to_the_homepage()
{
$user = factory(User::class)->create(['is_admin' => false]);
$response = $this->actingAs($user)->callMiddleware(AdminAuthenticate::class);
$response->assertRedirect(route('home'));
}
/** @test */
public function it_redirects_guests_to_the_homepage()
{
$response = $this->callMiddleware(AdminAuthenticate::class);
$response->assertRedirect(route('home'));
}
/** @test */
public function it_returns_a_forbidden_json_response_for_non_admin_users()
{
$user = factory(User::class)->create(['is_admin' => false]);
$response = $this->actingAs($user)->callMiddlewareJson(AdminAuthenticate::class);
$response->assertStatus(403);
}
/** @test */
public function it_returns_a_forbidden_json_response_for_guests()
{
$response = $this->callMiddlewareJson(AdminAuthenticate::class);
$response->assertStatus(403);
}
}
Conclusion
With these helper methods and assertion you can test your middleware in a readable way with a lot of flexibility. If you have any questions or middleware test cases that are not solved by these helpers, leave a comment below!