Laravel 4 - Тестирование шаблонов репозитория с помощью PHPUnit и Mockery

Я работаю над приложением для клиента, но у меня проблемы с тестированием репозиториев.

Чтобы привязать репозиторий к модели, у меня есть следующий код:

<?php

namespace FD\Repo;

use App;
use Config;

/**
 * Service Provider for Repository
 */
class RepoServiceProvider extends \Illuminate\Support\ServiceProvider
{
    public function register()
    {
        $app = $this->app;

        $app->bind('FD\Repo\FactureSst\FactureSstInterface', function ($app) {
            return new FactureSst\EloquentFactureSst(App::make('FactureSst'), new \FD\Service\Cache\LaravelCache($app['cache'], 'factures_sst', 10));
        });
    }
}

Затем репозиторий расширяет абстрактный класс, содержащий функции красноречивой ORM (найти, где, все и т. Д.). Код репозитория выглядит примерно так:

<?php

namespace FD\Repo\FactureSst;

use Illuminate\Database\Eloquent\Model;
use FD\Repo\AbstractBaseRepo;
use FD\Repo\BaseRepositoryInterface;
use FD\Service\Cache\CacheInterface;
use Illuminate\Support\Collection;

class EloquentFactureSst extends AbstractBaseRepo implements BaseRepositoryInterface, FactureSstInterface
{
    protected $model;
    protected $cache;

    public function __construct(Model $resource, CacheInterface $cache)
    {
        $this->model = $resource;
        $this->cache = $cache;
    }

    /**
     * Retrieve factures with the given SST and BDC IDs.
     *
     * @param int $sst_id
     * @param int $bdc_id
     * @return \Illuminate\Support\Collection
     */
    public function findWithSstAndBdc($sst_id, $bdc_id)
    {
        $return = new Collection;

        $factures = $this->model->where('id_sst', $sst_id)
            ->whereHas('facture_assoc', function ($query) use ($bdc_id) {
                $query->where('id_bdc', $bdc_id);
            })
            ->get();

        $factures->each(function ($facture) use (&$return) {
            $data = [
                'facture_id'   => $facture->id,
                'facture_name' => $facture->num_facture,
                'total_dsp'    => $facture->total_dsp(),
                'total_tradi'  => $facture->total_tradi()
            ];

            $return->push($data);
        });

        return $return;
    }
}

Чтобы проверить вызовы базы данных, я использую Mockery, так как вызовы базы данных будут слишком длинными. Вот мой тестовый класс:

<?php namespace App\Tests\Unit\Api\FactureSst;

use App;
use FactureSst;
use Illuminate\Database\Eloquent\Collection;
use Mockery as m;
use App\Tests\FdTestCase;

class FactureSstTest extends FdTestCase
{
    /**
     * The primary repository to test.
     */
    protected $repo;

    /**
     * Mocked version of the primary repo.
     */
    protected $mock;

    public function setUp()
    {
        parent::setUp();
        $this->repo = App::make('FD\Repo\FactureSst\FactureSstInterface');
        $this->mock = $this->mock('FD\Repo\FactureSst\FactureSstInterface');
    }

    public function tearDown()
    {
        parent::tearDown();
        m::close();
    }

    public function mock($class)
    {
        $mock = m::mock($class);
        $this->app->instance($class, $mock);
        return $mock;
    }

    public function testFindingBySstAndBdc()
    {
        $this->mock->shouldReceive('where')->with('id_sst', 10)->once()->andReturn($this->mock);
        $this->mock->shouldReceive('whereHas')->with('facture_assoc')->once()->andReturn($this->mock);
        $this->mock->shouldReceive('get');

        $result = $this->repo->findWithSstAndBdc(30207, 10);
        $this->assertEquals($result, new \Illuminate\Support\Collection);
        $this->assertEquals($result->count(), 0);
    }
}

Как видно из теста, я просто пытаюсь вызвать функцию и убедиться, что функции связаны правильно. Однако я продолжаю получать сообщение об ошибке:

App \ Tests \ Unit \ Api \ FactureSst \ FactureSstTest :: testFindingBySstAndBdc Mockery \ Exception \ InvalidCountException: метод where ("id_sst", 10) из Mockery_0_FD_Repo_FactureSst_FactureSstInterface должен вызываться ровно 1 раз, но вызываться ровно 1 раз.

Пожалуйста, помогите мне понять, почему это не работает и как это исправить. Извините, что код на французском языке, приложение предназначено для французского клиента.

Заранее спасибо.


person xonorageous    schedule 10.04.2015    source источник


Ответы (2)


Похоже, что вы издеваетесь над самим репозиторием, хотя вам действительно нужно высмеивать зависимости этого репозитория, а именно Illuminate\Database\Eloquent\Model, в котором вы собираетесь попасть в базу данных.

Измените setUp() так, чтобы он создавал фиктивный объект Illuminate\Database\Eloquent\Model, а затем заставлял его внедрять этот фиктивный объект при создании вашего репозитория.

public function setUp()
{
    parent::setUp();
    $this->mock = m::mock('Illuminate\Database\Eloquent\Model');  // Or better if you mock 'FactureSst'
    $this->app->instance('FactureSst', $this->mock);
}

Это немного сбивает с толку, потому что вы заявляете, что абстрактный класс содержит методы ORM, но когда вы вызываете эти методы, вы вызываете их на внедренной зависимости Model, а не на абстрактный класс. Вероятно, в этом и заключается путаница.

Кроме того, если ваши модели расширяют Illuminate\Database\Eloquent\Model, часто лучше просто вставить вашу модель в репозиторий, а не Illuminate\Database\Eloquent\Model. Таким образом, вы также можете воспользоваться любыми функциями отношений, которые вы настроили в своей модели внутри своего репозитория.

person user1669496    schedule 10.04.2015
comment
Привет, спасибо за ответ. Как вы сказали, у меня есть абстрактный класс, содержащий методы ORM, поэтому я должен делать вызовы модели, которую мне придется изменить. Я проверю это во вторник, когда вернусь на работу, спасибо за ответ - person xonorageous; 12.04.2015
comment
Только что проверил, вызвав абстрактные методы репо, а не модель, тесты работают. Большое спасибо - person xonorageous; 14.04.2015

Если не ошибаюсь, эта строчка

$result = $this->repo->findWithSstAndBdc(30207, 10);

на самом деле должно быть

$result = $this->mock->findWithSstAndBdc(30207, 10);

Также имейте в виду, что вы можете имитировать целую цепочку вызовов (полезно для подобных запросов):

$this->mock->shouldReceive('where->whereHas->get')
     ->once()->andReturn(/*MAKE A FAKE OBJECT HERE*/);

Я бы также использовал встроенный PHP foreach () вместо использования $ factures-> each, так как тогда на одну вещь меньше для тестирования.

person Alexander Kerchum    schedule 10.04.2015
comment
Привет, спасибо за ответ. Как и предполагалось, я попытался заменить $ this- ›repo на $ this-› mock, но теперь получаю следующую ошибку: BadMethodCallException: Метод Mockery_0_FD_Repo_FactureSst_FactureSstInterface :: findWithSstAndBdc () не существует для этого фиктивного объекта - person xonorageous; 10.04.2015