Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking Laravel Eloquent models - how to set a public property with Mockery

I want to use a mock object (Mockery) in my PHPUnit test. The mock object needs to have both some public methods and some public properties set. The class is a Laravel Eloquent model. I tried this:

$mock = Mockery::mock('User');
$mock->shouldReceive('hasRole')->once()->andReturn(true); //works fine
$mock->roles = 2; //how to do this? currently returns an error
$this->assertTrue(someTest($mock));

... but setting the public property returns this error:

BadMethodCallException: Method Mockery_0_User::setAttribute() does not exist on this mock object

This error is not returned when mocking a simple class, but is returned when I try to mock an Eloquent model. What am I doing wrong?

like image 752
mtmacdonald Avatar asked Apr 07 '14 10:04

mtmacdonald


5 Answers

If you want getting this property with this value, just use it:

$mock->shouldReceive('getAttribute')
    ->with('role')
    ->andReturn(2);

If you call $user->role you will get - 2 ($user - its User mock class)

like image 55
Joyful Avatar answered Nov 15 '22 18:11

Joyful


This answer is a bit late but hopefully it will help someone. You can currently set a static property on mocked Eloquent objects by using the 'alias' keyword:

$mocked_model = Mockery::mock('alias:Namespace\For\Model');
$mocked_model->foo = 'bar';
$this->assertEquals('bar', $mocked_model->foo);

This is also helpful for mocking external vendor classes like some of the Stripe objects.

Read about 'alias' and 'overload' keywords: http://docs.mockery.io/en/latest/reference/startup_methods.html

like image 27
Fancypants_MD Avatar answered Nov 15 '22 18:11

Fancypants_MD


To answer your question, you could also try something like this:

$mock = Mockery::mock('User');
$mock->shouldReceive('hasRole')->once()->andReturn(true); //works fine
$mock->shouldReceive('setAttribute')->passthru();
$mock->roles = 2; 

$mock->shouldReceive('getAttribute')->passthru();
$this->assertEquals(2, $mock->roles);

Or, as suggested by seblaze, use a partial mock:

$mock = Mockery::mock('User[hasRole]');
$mock->shouldReceive('hasRole')->once()->andReturn(true);
$mock->roles = 2; 
$this->assertEquals(2, $mock->roles);

But, from your code snippet, if you're writing unit tests, you should really only make one assertion per each test:

function test_someFunctionWhichCallsHasRole_CallsHasRole() {
    $mock = Mockery::mock('User');
    $mock->shouldReceive('hasRole')->once();

    $mock->someFunctionWhichCallsHasRole();
}

function test_someFunctionWhichCallsHasRole_hasRoleReturnsTrue_ReturnsTrue() {
    $mock = Mockery::mock('User');
    $mock->shouldReceive('hasRole')->once()->andReturn(true);

    $result = $mock->someFunctionWhichCallsHasRole();

    $this->assertTrue($result);
}        
like image 3
awei Avatar answered Nov 15 '22 18:11

awei


Tried this? It should cover you issue.

https://github.com/padraic/mockery/blob/master/docs/11-MOCKING-PUBLIC-PROPERTIES.md

I'd say implement these

protected $roles = array();

public function setRoles($roles)
{
    $this->roles = $roles;
}

public function addRole($role)
{
    $this->roles[] = $role;
}

Then you can test using:

$mock = Mockery::mock('User');
$mock->shouldReceive('hasRole')->once()->andReturn(true);
$mock->addRole(2);
$this->assertTrue(someTest($mock));

This apse gives you the opportunity to promise a format when you do a getRoles() which would be array of Role object if you do SOLID OOP, or if you rather use array, then at least you know it's always an array you get.

like image 1
Oldek Avatar answered Nov 15 '22 17:11

Oldek


Spy is your friend on this:

$mock = Mockery::spy('User');
$mock->shouldReceive('hasRole')->once()->andReturn(true);
$mock->roles = 2;
$this->assertTrue(someTest($mock));
like image 1
Yevgeniy Afanasyev Avatar answered Nov 15 '22 18:11

Yevgeniy Afanasyev