Question

I am unit testing my Laravel 4 Controller by mocking my repository that the controller expects. The problem is with the "store" function. This is the function that is called by Laravel when I do a POST to the given controller. The function gets called, but it is expected itemData as an input but I don't know how to provide that. Here is what I've tried:

ItemEntryController

class ItemEntryController extends BaseController
{
    protected $itemRepo;

    public function __construct(ItemEntryRepositoryInterface $itemRepo)
    {
        $this->itemRepo = $itemRepo;
    }

    public function store()
    {
        if(Input::has('itemData'))
        {
            $data = Input::get('itemData');

            return $this->itemRepo->createAndSave($data);
        }
    }
}

Test class

<?php

use \Mockery as m;

class ItemEntryRouteAndControllerTest extends TestCase {

protected $testItemToStore = '{"test":12345}';

public function setUp()
{
    parent::setUp();
    $this->mock = $this->mock('Storage\ItemEntry\ItemEntryRepositoryInterface');
}

public function mock($class)
{
    $mock = m::mock($class);
    $this->app->instance($class, $mock);

    return $mock;
}

public function testItemStore()
{
    Input::replace($input = ['itemData' => $this->testItemToStore]);

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

    $this->call('POST', 'api/v1/tools/itementry/items');
}
Was it helpful?

Solution

Well, you got a few options.

Integration testing

You may want to follow the unit testing docs, which actually has a call() method which allows you set all of this. This bootstraps the app and will use your databases, etc.

This is more of an integration test than unit test, as it uses your actual class implementations.

This may actually be preferable, as Unit testing controllers may not actually make much sense (it doesn't do much, in theory, but call other already-unit-tested classes). But this gets into unit testing vs integration testing vs acceptance testing and all the nuances that apply therein. (Read up!)

Unit Testing

If you're actually looking to unit test, then you need to make your controller unit-testable (ha!). This (likely) means injecting all dependencies:

class ItemEntryController extends BaseController
{
    protected $itemRepo;

    // Not pictured here is actually making sure an instance of
    // Request is passed to this controller (via Service Provider or
    // IoC binding)
    public function __construct(ItemEntryRepositoryInterface $itemRepo, Request $input)
    {
        $this->itemRepo = $itemRepo;
        $this->request = $input;
    }

    public function store()
    {
        if($this->input->has('itemData'))
        {
            // Get() is actually a static method so we use
            // the Request's way of getting the $_GET/$_POST variables
            // see note below!
            $data = $this->input->input('itemData');

            return $this->itemRepo->createAndSave($data);
        }
    }
}

Sidenote: The Input facade is actually an instance of Request objet with an extra static method get()!

So now that we aren't using Input any longer, and are injecting the Request object, we can unit test this class by mocking the Request object.

Hope that helps!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top