I am trying to write unit tests for my middleware in Laravel. Does anyone know a tutorial, or have an example of this ?
I have been writing a lot of code, but there must be a better way to test the handle method.
I am trying to write unit tests for my middleware in Laravel. Does anyone know a tutorial, or have an example of this ?
I have been writing a lot of code, but there must be a better way to test the handle method.
Using Laravel 5.2, I am unit testing my middleware by passing it a request with input and a closure with assertions.
So I have a middleware class GetCommandFromSlack
that parses the first word of the text
field in my Post (the text from a Slack slash command) into a new field called command
, then modifies the text
field to not have that first word any more. It has one method with the following signature: public function handle(\Illuminate\Http\Request $request, Closure $next)
.
My Test case then looks like this:
use App\Http\Middleware\GetCommandFromSlack;
use Illuminate\Http\Request;
class CommandsFromSlackTest extends TestCase
{
public function testShouldKnowLiftCommand()
{
$request = new Illuminate\Http\Request();
$request->replace([
'text' => 'lift foo bar baz',
]);
$mw = new \App\Http\Middleware\GetCommandFromSlack;
$mw->handle($request,function($r) use ($after){
$this->assertEquals('lift', $r->input('command'));
$this->assertEquals('foo bar baz',$r->input('text'));
});
}
}
I hope that helps! I'll try to update this if I get more complicated middleware working.
To actually test the middleware class itself you can do:
public function testHandle()
{
$user = new User(['email'=>'...','name'=>'...']);
/**
* setting is_admin to 1 which means the is Admin middleware should
* let him pass, but oc depends on your handle() method
*/
$user->is_admin = 1;
$model = $this->app['config']['auth.model'];
/**
* assuming you use Eloquent for your User model
*/
$userProvider = new \Illuminate\Auth\EloquentUserProvider($this->app['hash'], $model);
$guard = new \Illuminate\Auth\Guard($userProvider, $this->app['session.store']);
$guard->setUser($user);
$request = new \Illuminate\Http\Request();
$middleware = new \YourApp\Http\Middleware\AuthenticateAdmin($guard);
$result = $middleware->handle($request, function(){ return 'can access';});
$this->assertEquals('can access',$result);
}
I thinking the best solution is just checking what happened after middleware. For example, the authentication middleware:
<?php namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
class Authenticate {
/**
* The Guard implementation.
*
* @var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* @param Guard $auth
* @return void
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($this->auth->guest())
{
if ($request->ajax())
{
return response('Unauthorized.', 401);
}
else
{
return redirect()->guest('auth/login');
}
}
return $next($request);
}
}
And my test unit:
<?php
class AuthenticationTest extends TestCase {
public function testIAmLoggedIn()
{
// Login as someone
$user = new User(['name' => 'Admin']);
$this->be($user);
// Call as AJAX request.
$this->client->setServerParameter('HTTP_X-Requested-With', 'XMLHttpRequest');
$this->call('get', '/authpage');
$this->assertEquals(200, $response->getStatusCode());
}
}
I would do it in that way.
I was working on a localization Middleware that sets the app locale based on a URI segment, e.g. http://example.com/ar/foo
should set the app local to Arabic. I basically mocked the Request
object and tested as normal. Here is my test class:
use Illuminate\Http\Request;
use App\Http\Middleware\Localize;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class LocalizeMiddlewareTest extends TestCase
{
protected $request;
protected $localize;
public function setUp()
{
parent::setUp();
config(['locale' => 'en']);
config(['app.supported_locales' => ['en', 'ar']]);
$this->request = Mockery::mock(Request::class);
$this->localize = new Localize;
}
/** @test */
public function it_sets_the_app_locale_from_the_current_uri()
{
$this->request->shouldReceive('segment')->once()->andReturn('ar');
$this->localize->handle($this->request, function () {});
$this->assertEquals('ar', app()->getLocale());
}
/** @test */
public function it_allows_designating_the_locale_uri_segment()
{
$this->request->shouldReceive('segment')->with(2)->once()->andReturn('ar');
$this->localize->handle($this->request, function () {}, 2);
$this->assertEquals('ar', app()->getLocale());
}
/** @test */
public function it_throws_an_exception_if_locale_is_unsupported()
{
$this->request->shouldReceive('segment')->once()->andReturn('it');
$this->request->shouldReceive('url')->once()->andReturn('http://example.com/it/foo');
$this->setExpectedException(
Exception::class,
"Locale `it` in URL `http://example.com/it/foo` is not supported."
);
$this->localize->handle($this->request, function () {});
}
}
And here is my Middleware class:
namespace App\Http\Middleware;
use Closure;
class Localize
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param integer $localeUriSegment
* @return mixed
*/
public function handle($request, Closure $next, $localeUriSegment = 1)
{
$locale = $request->segment($localeUriSegment);
if (in_array($locale, config('app.supported_locales')))
{
app()->setLocale($locale);
}
else
{
abort(500, "Locale `{$locale}` in URL `".$request->url().'` is not supported.');
}
return $next($request);
}
}
Hope that helps :)