Yii2 ActiveRecord mocking with Mockery
Asked Answered
P

2

8

I'm running phpunit with mockery (without DB/fixtures), but I have a trouble with mocking a model.

$customer = Mockery::mock(CustomerModel::class);
echo $customer->id;

Produces error:

BadMethodCallException: Method Mockery_1_models_Customer::hasAttribute() does not exist on this mock object

Then I tried:

$customer->shouldReceive('hasAttribute')->andReturn(true);

But again, I run in to:

Fatal error: Call to a member function getDb() on a non-object in ..\yiisoft\yii2\db\ActiveRecord.php on line 135

Any suggestions?

Pitsaw answered 3/7, 2015 at 11:28 Comment(0)
H
4

I know you are not accepting fixture answers, but I could not figure it out either. The problem I was trying to solve actually was mocking some methods in model so to avoid creating underlying fixtures.

So I ended up using Proxy pattern implementation in Mockery

private $_product;
public function testMe() 
{
   // Here we use fixtured instance of Product model to build a Proxy
   $this->_product = \Mockery::mock($this->product('product1')); 
   // somehow model attributes are inaccessible via proxy, but we can set them directly as a Proxy property
   $this->_product->id = 1; 
   $this->_product->shouldReceive('getPrice')->andReturn(1000);
   // assertions below
   ...
}

In this example getPrice() method from Product model returns Product price from related tables. And we mock it here so that we won't have to populate db with all related models fixtures. Nevertheless Product itself is still a fixture.

Maybe not the best solution, but managed to save me some CPU time while keeping unit tests decoupled.

Docs here http://docs.mockery.io/en/latest/reference/partial_mocks.html

update:

I also made a small helper to solve attribute proxying problem

/**
 * @param \yii\base\Model $model
 * @return \Mockery\MockInterface
 */
private function setupMock($model)
{
    $mock = \Mockery::mock($model);
    foreach ($model->getAttributes() as $key => $value) {
        $mock->$key = $value;
    }
    return $mock;
}

This way all attributes and their corresponding values from original model become available in mock.

Health answered 12/7, 2015 at 15:17 Comment(0)
L
1

Is Traditional Partial Mock or Passive Partial Mock what you are looking for?

$customer = Mockery::mock(CustomerModel::className())->makePartial();
echo $customer->id;

The sample above would of course not return anything since id is not set, but wont throw any errors.

Ludhiana answered 13/7, 2015 at 11:51 Comment(1)
I think this is a problem in the first place. This approach creates new CustomerModel instance which is not persistent in terms of ActiveRecord. At the same time you cannot mock id because somehow $customer->id = 123 won't assign valueHealth

© 2022 - 2024 — McMap. All rights reserved.