0

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?

1 Answer 1

4

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Sure, makes total sense. Thank's for the answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.