7
votes

I am trying to set up the simplest of tests in my controller but, as with most things Laravel, there are no decent tutorials to demonstrate the simple stuff.

I can run a simple test (in a file called UserControllerTest) like this:

public function testIndex()
{      
    $this->call('GET', 'users');
    $this->assertViewHas('users');
}

This calls the /users route and passes in an array users.

I want to do the same with Mockery but how?

If I try this:

public function testIndex()
{
  $this->mock->shouldReceive('users')->once();

  $this->call('GET', 'users');

}

I get an error that "Static method Mockery_0_users::all does not exist on this mock object.

Why not? I am mocking User which extends Ardent and in turn extends Eloquent. Why does ::all not exist for the mock?

BTW, these are the set-up functions for Mockery:

public function setUp()
{
  parent::setUp();

  $this->mock = $this->mock('User');
}

public function mock($class)
{
  $mock = Mockery::mock($class);

  $this->app->instance($class, $mock);

  return $mock;
}
2

2 Answers

9
votes

You can't directly mock an Eloquent class. Eloquent is not a Facade and your User model neither. There is a bit of magic in Laravel but you can't do things like that.

If you want to mock your User class, you have to inject it in the controller constructor. The repository pattern is a good approach if you want to do that. There is a lot of articles about this pattern and Laravel on Google.

Here some pieces of code to show you how it could look like :

class UserController extends BaseController {

    public function __construct(UserRepositoryInterface $users)
    {
        $this->users = $users;
    }

    public function index()
    {
        $users = $this->users->all();
        return View::make('user.index', compact('users'));
    }

}

class UserControllerTest extends TestCase
{

    public function testIndex()
    {
        $repository = m::mock('UserRepositoryInterface');
        $repository->shouldReceive('all')->andReturn(new Collection(array(new User, new User)));
        App::instance('UserRepositoryInterface', $repository);

        $this->call('GET', 'users');
    }

}

If it seems to be too much structuration for your project you can just call a real database in your tests and don't mock your model classes... In a classic project, it just works fine.

1
votes

This function is part of a project called apiato.io you can use it to mock any class in Laravel, even facade, basically anything that can be resolved with the IoC, which is almost all classes if you are using proper dependency injection:

/**
 * Mocking helper
 *
 * @param $class
 *
 * @return  \Mockery\MockInterface
 */
public function mock($class)
{
    $mock = Mockery::mock($class);
    App::instance($class, $mock);

    return $mock;
}