theilen's avatar

PhpSpec: How to test Eloquent methods?

I have an simplified repository like this:

class RoleRepository implements RoleRepositoryInterface {

    private $role;

    function __construct(Role $role)
    {
        $this->role = $role;
    }

    public function getList()
    {
        return $this->role->orderBy('id')->lists('role', 'id');
    }

Now I want to test the getList-method with PhpSpec, the spec looks like this:

class RoleRepositorySpec extends ObjectBehavior
{
    function let(Role $role)
    {
        $this->beConstructedWith($role);        
    }

    public function it_gets_an_ordered_list_of_roles(Role $role)
    {
        $role->orderBy('id')->shouldBeCalled();
        $role->lists('role', 'id')->shouldBeCalled();

        $this->getList();
    }
}

But of course that doesn't work, PhpSpec tells me

method `Double\[...]\Role\P81::orderBy()` is not defined

Is there a way to test these methods?

As much as a like the rigid way of PhpSpec most of the times, sometimes I find it a real pain in the neck... ;-)

0 likes
7 replies
fenos's avatar

Because Obviously your repository doesn't have the method OrderBy, that method belongs to the DatabaseManager Class, so instead Mock the Database Manage injecting it as paramenter, and it will look something like so:

 public function it_gets_an_ordered_list_of_roles(Role $role, DatabaseManager $db)
    {
        $db->orderBy('id')->shouldBeCalled()->willReturn($db);
        $db->lists('role', 'id')->shouldBeCalled()->willReturn([]);

        $this->getList()->shouldBe([]);
    }
theilen's avatar

That doesn't work:

 method `Double\Illuminate\Database\DatabaseManager\P82::orderBy()` is not defined

And as far as I understand PhpSpec, in my spec I say that I want a method orderBy to be called on the injected Role object (which is basically a eloquent model) and not on the RoleRepository.

JeffreyWay's avatar

Even if you could test it in this way, it wouldn't be a good test. Too many implementation details in your spec.

Instead, integration test your repositories.

2 likes
fenos's avatar

@theilen Yhea I think is not the DatabaseManager to own those methods either, but how jeffrey said, is much more clever, go to make Integration Tests for your repositories. I suggest to use Codeception in couple with TestDummy Package of jeffrey!

theilen's avatar

Hm, okay, I have to think about that.

But just to make things clear for me: Is there a way I could make my test work with PhpSpec (even if this test won't be a good one)? Or is it simply impossible to test calls to these methods this way?

JeffreyWay's avatar
Level 59

@theilen - It's very difficult to unit test these types of things. Technically, you could do it, but it would be a bit of a nightmare. You'd end up reproducing the exact implementation code within your test, which is really bad.

Test your repositories against a database - maybe a sqlite db in memory, to make it as fast as possible. By doing so, you also get the bonus of your tests looking like actual documentation.

/** @test */
function it_gets_a_list_of_roles()
{
    // Populate db with a couple test rows

    // Perform the query
    $list = $this->getList();

    // Make sure it looks the way you expect
    $this->assertEquals(...);
}
3 likes
theilen's avatar

Yep, that's what I ended up with. A big pile of test code that almost reproduces the production code exactly. And that really didn't test a lot...

Thanks for conforming that sometimes a sponge is the wrong tool to drive a nail into a wall... :-)

Please or to participate in this conversation.