Разнесите создание инстанса и его использование.
class BarFabric
{
public function create(array $config = [])
{
return new Bar($config);
}
}
class Foo
{
protected $barFabric;
public function __construct(BarFabric $barFabric)
{
$this->barFabric = $barFabric;
}
public function someMethod()
{
$bar = $this->barFabric->create();
$bar->method1();
$bar->method2();
$blabla = $bar->getResult();
//etc
}
}
class FooTest
{
public function testSomeMethod()
{
$bar = \Mokery::mock(Bar::class);
// ... описание поведения для мока
$factory = \Mokery::mock(BarFactory::class);
$factory->shouldReceive('create')->andReturn($bar);
$foo = new Foo($factory);
$this->assertSomething($foo);
}
}
О том, как быть с ActiveQuery...
Во первых, выносите логику запроса так же в отдельный класс. Если генерируете новую модель, Gii это может сделать и сам.
class FooQuery extend ActiveQuery
{
/**
* @inheritdoc
* @return Foo[]|array
*/
public function all($db = null)
{
return parent::all($db);
}
/**
* @inheritdoc
* @return Foo|array|null
*/
public function one($db = null)
{
return parent::one($db);
}
}
Этот объект передавайте в целевой класс так же как фабрику:
class Bar
{
protected $query;
public function __construct(FooQuery $query)
{
$this->query = $query;
}
public function someMethod()
{
$foo = $this->query->where(...)->one();
$foo->doSomething();
}
}
Ну а дальше мокайте как и в первом случае
$queryMock = \Mockery::mock(FooQuery::class);
$queryMock->shouldRecieve('where->one')->andReturn($fooMock);
ActiveQuery можно мокать частично, после этого будут выполняться все родные методы, а вот сохранение в базу пропустится.
$fooMock = \Mockery::mock(Foo::class.'[save, update]');
$fooMock->shouldRecieve('save', 'update')->andReturn(true);
Relations можно не подменять вообще. Они прекрасно подставляются через
ActiveRecord::populateRelation().
$foo = new Foo();
$foo->populateRelation('bar', new Bar());