Recently, when working on a project for a 13 year old code base built on a custom framework, we switched from using magic methods retrieving model attributes to actual getter and setter methods. The reason we switched was because we wanted to start writing unit tests while using Mockery, but quickly realized we couldn’t mock magic methods!
Up to this point I had not really spent time writing unit tests in any of my Laravel applications, but despite that, knew that something wasn’t entirely adding up. There is no way that a Laravel app was not unit testable or that models could not be mocked. However, I didn’t have the experience yet and spent some time researching it but couldn’t really find anything concrete or at least nothing that clicked for me.
As I write this, the light just came on! I just implemented a unit test in a Laravel application which mocks models and relies on the public accessors. So it is possible, and it was easy!
So, what’s the difference? Did it take some Laravel “magic” to make it work?
The key difference between the legacy magic methods and Laravel is that the magic method in Laravel calls a method to perform the logic
getAttribute for getting the value and
setAttribute for setting the value.
These methods are easily mock-able! Once I realized this, it was so obvious to me and I can’t believe I hadn’t realized it before.
$mock->shouldReceive('getAttribute') ->with($property) ->andReturn($propertyValue);
If you are only relying on a single property in your test then you can opt out of the
->with($property) method call, but using it is more descriptive so that’s what I’ll be doing moving forward.