Question

I need to test a repository, which has a Eloquent model injected via constructor.

class EloquentOrderRepository implements OrderRepositoryInterface
{

    protected $model;

    public function __construct(Order $model)
    {
        $this->model = $model;
    }

    public function calculateValues(array $deliveryOption = null)
    {
        if (! is_null($deliveryOption)) {
            $this->model->value_delivery = (float) number_format($deliveryOption['price'], 2);
        }

        $this->model->value_products = (float) number_format($this->model->products->getTotal(), 2);
        $this->model->value_total    = (float) $this->model->value_products + $this->model->value_delivery;
    }
}

My problem is when I call $this->model->value_products (or any of the attributes). The Eloquent model try to call the setAttribute method, which doesn't exist on the mocked model. If I mock this method, I can't set the attribute correctly, and my test assertions will fail.

Here is my test:

<?php
class EloquentOrderRepositoryTest extends \PHPUnit_Framework_TestCase
{

    protected $model, $repository;

    public function setUp()
    {
        $this->model = Mockery::mock('Order');
    }

    public function test_calculate_values()
    {
        $repository = new EloquentOrderRepository($this->model);

        $this->model->products = m::mock('SomeCollection');
        $this->model->products->shouldReceive('getTotal')->once()->withNoArgs()->andReturn(25);

        $this->model->calculateValues(array('price' => 12));
        $this->assertEquals(12, $this->model->value_delivery);
        $this->assertEquals(25, $this->model->value_products);
        $this->assertEquals(37, $this->model->value_total);
    }
}

Any thoughts on this?

Was it helpful?

Solution

I think your main issue is that you're not using the repository pattern correctly. You should think about the passed model in your constructor as a prototype. It's not a real thing to be worked with, but an instance of something you use for other things. In the repository, you may have a method getUnpaidOrders which will do something like return $this->model->wherePaid('0')->get();. As you can see, we're not interacting with the instance as an actual concrete instance but more of something to achieve a broader scope.

In your calculate method you're actually setting values on this prototype model. I don't know what you then intend to do with these but as far as I'm aware this is not what the repository patter is supposed to do. The methods on a repository are generally static-like methods, where you call them (maybe with some input) and get something back. They shouldn't have an effect on any kind of internal state as a repository shouldn't have any kind of internal state.

Hopefully this makes sense.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top