Laravel mockery
Asked Answered
D

2

8

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;
}
Declarant answered 8/9, 2014 at 12:33 Comment(0)
P
10

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.

Perfection answered 8/9, 2014 at 14:29 Comment(2)
Thanks for the response. I am tempted to stay away from Mockery for now but what happens if I create a database, it works fine, goes into production, etc. and then the user reports an error. If I don't mock and run some tests, will I not be inserting and updating data on the actual users database?Declarant
You have to create a specific configuration for your testing environment. Just create an app/config/testing/database.php file with a configuration for an other database which will be used only when you will run your tests. Read the doc if you want more details : laravel.com/docs/configuration#environment-configuration.Perfection
A
1

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;
}
Asphaltite answered 28/3, 2017 at 15:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.