Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TDD and the use of Views in Laravel 5

Even after implementing the Repository pattern to abstract the data access layer (Eloquent ORM), when you do something like:

$students = StudentRepository::all();
return view('students.index', ['students' => $students]);

You end up sending either an \Eloquent\Model subclass or an Eloquent\Collection to the view.

This means that if your view attempts to do lazy-loading and you run tests cases on a CI server, it's gonna crash due the lack of a database connection.

This problem leads to the following solutions:

  1. You mock directly Eloquent subclasses in your tests, so that when it attempts to lazy load, you can set the returned value
  2. You implement interfaces for each model and bind them using laravel's IoC.

The disadvantages for this 2 options would be:

For option 1: If you end up mocking Eloquent on your tests, then what's the point of implementing repository pattern (which seems to be very popular due to the "flexibility" if you have to change from Eloquent to a different ORM), since you're gonna have to re-write your tests too.

For option 2: If you write interfaces for every Eloquent\Model, the only thing you'd have to do is write extra code for setters and getters.

If this far I'm still on the right path, option 2 would be the best choice but there's absolutely not a single thread or info regarding abstracting Eloquent\Model into interfaces to mock calls made by the view that are either lazy-loaded or just properties.

So why is this? Am I not seeing something? maybe tests are supposed to exit before rendering the view (and $this->assertViewHas($variable) is pointless)? maybe there's a way to ignore crashes when it occurs in the view? maybe people only unit test JSON API controllers?

like image 447
Christopher Francisco Avatar asked Apr 21 '15 19:04

Christopher Francisco


1 Answers

Is it a bird? Is it a plane? No, it's the rarely used and barely documented Laravel Fluent class to the rescue!

The Fluent class allows you to emulate all of the functionality of a model or other abstract class with attributes, but without carrying around the weight or dependencies of the model class itself. It's very similar to classes that you might build as entity classes when you are using a data mapper ORM like Doctrine instead of Laravel's Eloquent.

A Fluent object can be built by providing an array of attributes as key=>value pairs like this:

$myobject = new Fluent(['id' => 666]);

You can access the attributes using magic setters and getters just like a model object:

$myobject->id = 999;
$myId = $myobject->id;

and furthermore, because the Laravel Model class is Arrayable and has a toArray() function you can use these methods to create your Fluent object.

$myobject = new Fluent($mydatamodel->toArray());

By using fluent in your Repository classes you can remove the dependency on the Laravel model objects from your views but still have the convenience of the magic setters and getters. Of course there's a little more jiggery-pokery in taking the results of MyModel::all() and turning that into an array of Fluents but in my case I wrote a trait to do that and attached it to my models. I also added a trait to populate a model directly from a Fluent but that's an exercise left to the reader.

http://laravel.com/api/5.1/Illuminate/Support/Fluent.html

like image 76
delatbabel Avatar answered Sep 21 '22 03:09

delatbabel