Laravel mock with Mockery Eloquent models

24,404

Solution 1

The problem was in $this->createApplication();.

I have commented that line and Input::all() works fine with all input parameters!

Solution 2

The way you are testing this, in the controller constructor is passed an instance of the real Eloquent model, not the mock. If you want to test the facades (as clearly stated in the documentation) you should call the shouldReceive() method directly on the facade to have it mocked.

But, since you are redefining the $this->user variable in the store() method of the controller, it will not be called, unless you remove the hardcoded instantiation and use the injected $user.

Edit: i overlooke the $this->app->instance('User', $this->mock); line. So, the problem may be due the fact that in the store method you are getting a new class instance directly, and not via the Laravel IoC container. instead of $this->user = new User; in your store method, you should get it via App::make('User');

Solution 3

i had this same issue when i started testing..... the thing there is that, in your userscontroller store method you are actually saving the user to the database and base on your code it might work just fine but you are surprise that it is actually failing the test. Now think of it this way, you mock the user, and you told your test that when ever i call User model, please give me the mock version of my user, like you did in your code line below

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

Now the problem is that you need to also call the mock version of save() method from the through the mock version of User like so:

$this->mock->save();

Consider the following Example:

public function testStore()
{
    $input = ['name', 'Canaan'];

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

    $this->app->instance('User', $this->mock);
    $this->mock->create($input);

    $this->call('POST', 'users', $input);

}

i hope it helps you.

Share:
24,404
Matteo Codogno
Author by

Matteo Codogno

I'm Technical Leader at WellD, a software house delivering innovative solutions in the domains of Energy, Industry automation and Health. I love to experiment with new technologies, open source projects and design Software architectures (I am a kid in a candy store!).

Updated on July 09, 2022

Comments

  • Matteo Codogno
    Matteo Codogno almost 2 years

    I'm developing a PHP (5.4.25) application with laravel(4.2) framework. I'd like test my UserController with Mockery, so I've fit my UserController in this way:

    class UsersController extends \BaseController {
        protected $user;
    
        public function __construct(User $user) {
            $this->user = $user;
            $this->beforeFilter('csrf', array('on'=>'post'));
        }
    
        public function store() {
            $validator = Validator::make(Input::all(), User::$rules);
    
            if ( $validator->passes() ) {
                $this->user->username = Input::get('username');
                $this->user->password = Hash::make(Input::get('password'));
                $this->user->first_name = Input::get('first_name');
                $this->user->last_name = Input::get('last_name');
                $this->user->email = Input::get('email');
                $this->user->save();
    
    
                return true;
            } else {
                return false;
            }
        }
    

    I want mock Eloquent User model so i develop my UsersControllerTest so:

    class UsersControllerTest extends TestCase {
    
        private $mock;
    
        public function __construct() {}
    
        public function setUp() {
            parent::setUp();
    
            $this->createApplication();                                                     
        }
    
        public function tearDown() {
            parent::tearDown();
    
            Mockery::close();
        }
    
        public function testStore() {
            $this->mock = Mockery::mock('Eloquent','User[save]');                                           
            $this->mock
                ->shouldReceive('save')
                ->once()
                ->andReturn('true');                                                        
            $this->app->instance('User', $this->mock);                                      
    
            $data['username'] = 'qwerety';
            $data['first_name'] = 'asd';
            $data['last_name'] = 'asd123';
            $data['email'] = '[email protected]';
            $data['password'] = 'password';
            $data['password_confirmation'] = 'password';
    
            $response = $this->call('POST', 'users', $data);
    
            var_dump($response->getContent());
        }
    
    } 
    

    When I run my test it returns me this error:

    Mockery\Exception\InvalidCountException : Method save() from Mockery_0_User should be called
     exactly 1 times but called 0 times.
    

    Why? What's wrong?

    EDIT: I found the problem - If I don't use mock object all works fine and the controller create a new user in the DB, but when I use mock the Input:all() method returns empty array.

    -- Thanks