Unit Testing Laravel 4 w/ Cartalyst Sentry + Mockery

Joe • June 26, 2014

php testing laravel

I had a really hard time piecing this together so I thought I'd share it and maybe it would help someone (or me in 2 months when I forget all of this and stumble upon it again).

The use case: I have a Controller "BoxesController" that I want to unit test the store() method on. The thing I had to play with some was having the test act as an admin user because I was posting to a route only admins should be able to access.

The key to this is to set the user in the test to the user who should have access to post to our route (an Administrator).

...
    $user = Sentry::findUserByID(1);
    Sentry::setUser($user);
...

Something else that tripped me up was even though I was passing $input into the $this->mock...

...
    $this->mock
        ->shouldReceive('create')
        ->once()
        ->with($input);
...

I still had to pass $input to $this->call before the controller would see any input...

...
    $this->app->instance('Box', $this->mock);
    $this->call('POST', 'admin/boxes', $input);
...

Here is My BoxesControllerTest.php

class BoxesControllerTest extends TestCase {

    public function setUp()
    {
        parent::setUp();
        $this->mock = Mockery::mock('Eloquent', 'Box');

        $_ENV['DB_HOST'] = 'localhost';
        $_ENV['DB_NAME'] = 'homestead';
        $_ENV['DB_USERNAME'] = 'homestead';
        $_ENV['DB_PASSWORD'] = 'secret';

    }

    public function tearDown()
    {
        Mockery::close();
    }

    public function testStoreSuccess()
    {
        // Establish us as Admin (user_id == 1)
        $user = Sentry::findUserByID(1);
        Sentry::setUser($user);

        Input::replace($input = ['user_id' => '2 Title',
            'draft_id' => '2'
        ]);

        $this->mock
            ->shouldReceive('create')
            ->once()
            ->with($input);

        $this->app->instance('Box', $this->mock);

        $this->call('POST', 'admin/boxes', $input);

        $this->assertRedirectedToRoute('admin.boxes.index', null, ['flash']);
    }

}

Here is my BoxesController

class BoxesController extends \BaseController {

    protected $box;

    public function __construct(Box $box)
    {
        $this->box = $box;
    }
    
    public function store()
    {
        $input = Input::all();

        $rules = array(
            'user_id' => 'required',
            'draft_id' => 'required'
        );

        $validator = Validator::make($input, $rules);

        if ($validator->fails()) {

            return Redirect::route('admin.boxes.create')
                ->withErrors($validator)
                ->withInput($input);
        } else {
            $this->box->create($input);

            return Redirect::route('admin.boxes.index')->with('flash', 'Your box has been created!');
        }
    }

Thanks for reading, hopefully this helps save you some time.