How to mock Laravel's eloquent accessor attribute
Asked Answered
M

2

6

I'm implementing unit tests into my Laravel 4 application but I'm stuck on mocking accessor attributes.

I have an Eloquent model that has an Accessor attribute in it. I'm trying to mock this model and return a value when this accessor attribute is called. But I can't find any solution for it to make it work.

My super simple user class.

class User extends Eloquent {
    public function getFullNameAttribute() {
        return $this->first_name . ' ' . $this->last_name;
    }
}

I tried the following:

$user_mock = m::mock('MyApp\Models\User');

$user_mock->shouldReceive('__get')->with('full_name')->andReturn('John Snow'); // doesn't work
$user_mock->shouldReceive('getAttribute')->with('full_name')->andReturn('John Snow'); // doesn't work
$user_mock->shouldReceive('getFullNameAttribute')->andReturn('John Snow'); // doesn't work

echo $user_mock->full_name; // --> " "

I just get an empty space back, indicating that the original function is still being called.

Makebelieve answered 5/6, 2015 at 10:53 Comment(0)
K
2

Try not to mock eloquent models. This is an example of "mocking a type you don't own". If Laravel changes the eloquent internals in a new release, and you have mocked them in your unit tests, then your tests may provide false positives.

In addition I think it's odd to try and mock them. You lose the idea that these entities map to real world things, while it is the business logic components that need to be tested in isolation (with dependencies mocked).

When using accessors I have found it helpful to wrap it in another abstraction, for instance in a Repository layer:

public function getFirstNameAttribute()
{
     return app(UserRepository::class)->getFirstName($this);
}

I find accessors make the client code extremely readable. This is fully testable and the entire interaction with the database is mockable, which might be useful in reducing overhead if you have thousands of tests.

Karns answered 5/9, 2017 at 7:10 Comment(0)
D
-3

Looking at Mockery's documentation, it looks like it does not support mocking magic methods. You would basically just need to set the property using plain assignment:

$user_mock = m::mock('MyApp\Models\User');
$user_mock->first_name = 'John Snow';

Mockery docs

Discomfort answered 8/6, 2015 at 17:22 Comment(4)
Why is this downvoted? This doesn't work? Don't downvote with no clear reason and if you do please add a comment explaining.Photocomposition
Trying to statically set a model's attribute like a normal object will not work. since Laravel uses getter methods to access attributes.Blenheim
I disagree, creating partial mocks (M::mock('MyApp\Models\User')->makePartial(); does the trickEugeniaeugenics
If you're attempting to set the value to be returned from an accessor, this isnt going to help because the accessor is likely modifying the value before it is returned. So you likely wont receive the exact value you expect.Unseen

© 2022 - 2024 — McMap. All rights reserved.