Как правильно мокнуть коллекцию моделей laravel?

внутри аrtisan команды есть строка внутри конструктора

$this->transfers = TransferCrypto::where(["state" => 1])->with('ref_withdraw.user')->get();

как правильно её мокнуть в unit тесте без фабрик?

мой топорный вариант

    $mockQueryBuilder = Mockery::mock('Illuminate\Database\Eloquent\Builder');
    $mockQueryBuilder
        ->shouldReceive('where')
        ->with(['state' => 1])
        ->once()
        ->andReturnSelf();
    $mockQueryBuilder
        ->shouldReceive('with')
        ->with('ref_withdraw.user')
        ->once()
        ->andReturnSelf();
    $mockQueryBuilder
        ->shouldReceive('get')
        ->once()
        ->andReturn(collect([
            (object)[
                'id' => 1, 'state' => 1,
            ],
            (object)['id' => 2, 'state' => 1]
        ]));
    $mockTransferCrypto = Mockery::mock('overload:App\TransferCrypto'); // or 'alias'
    $mockTransferCrypto
        ->shouldReceive('where')
        ->with(['state' => 1])
        ->once()
        ->andReturn($mockQueryBuilder);

как правильно и красиво это зарефакторить?


Ответы (1 шт):

Автор решения: mydls1

Твой подход в целом верен, но его можно сделать более лаконичным и читаемым, убрав часть дублирующегося кода и улучшив структурированность. Вот пример рефакторинга:

  1. Объедини мокирование вызовов where, with и get в одну последовательность.
  2. Перемести мокирование метода where непосредственно на класс TransferCrypto.

Результат выглядит так:

// Мокируем запрос и его цепочку вызовов
$mockQueryBuilder = Mockery::mock('Illuminate\Database\Eloquent\Builder')
    ->shouldReceive('with')
    ->with('ref_withdraw.user')
    ->andReturnSelf()
    ->getMock();

$mockQueryBuilder
    ->shouldReceive('get')
    ->andReturn(collect([
        (object)[ 'id' => 1, 'state' => 1 ],
        (object)[ 'id' => 2, 'state' => 1 ]
    ]));

// Мокируем класс TransferCrypto и метод where
$mockTransferCrypto = Mockery::mock('overload:App\TransferCrypto')
    ->shouldReceive('where')
    ->with(['state' => 1])
    ->andReturn($mockQueryBuilder)
    ->getMock();
  1. Использование ->getMock(): помогает создать окончательный мок-объект без необходимости дополнительных вызовов.
  2. Цепочка мокирования: в Mockery можно сразу комбинировать последовательные вызовы, чтобы сделать код компактнее.
  3. Сокращение кода: определение и вызов with и get теперь находятся в одном блоке, что упрощает восприятие логики.
→ Ссылка