Laravel 5 - Использование насмешек для насмешки над моделью Eloquent

Просматривал весь интернет, но, похоже, не нашел ответа на мою проблему. Я погрузился в тестирование контроллеров в Laravel с использованием PHPUnit и Mockery. Тем не менее, мне кажется, что мои модели, основанные на Eloquent, не издевались правильно. Мне удавалось издеваться над Auth::user() таким же образом, хотя это не используется в тесте ниже.

Функция в AddressController, которую нужно протестировать:

public function edit($id)
{
    $user = Auth::user();
    $company = Company::where('kvk', $user->kvk)->first();
    $address = Address::whereId($id)->first();

    if(is_null($address)) {
        return abort(404);
    }

    return view('pages.address.update')
        ->with(compact('address'));
}

ControllerTest, содержащий setUp и метод mock

abstract class ControllerTest extends TestCase
{
    /**
     * @var \App\Http\Controllers\Controller
     */
    protected $_controller;

    public function setUp(){
        parent::setUp();
        $this->createApplication();
    }

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

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

AddressControllerTest, расширяющий ControllerTest

class AddressControllerTest extends ControllerTest
{
    /**
     * @var \App\Models\Address
     */
    private $_address;

    /**
     * @var \App\Http\Controllers\AddressController
     */
    protected $_controller;

    public function setUp(){
        parent::setUp();
        $this->_controller = new AddressController();
        $this->_address = factory(Address::class)->make();
    }

    public function testEdit404(){
        $companyMock = $this->mock(Company::class);
        $companyMock
            ->shouldReceive('where')
            ->with('kvk', Mockery::any())
            ->once();
            ->andReturn(factory(Company::class)->make([
                'address_id' => $this->_address->id
            ]));

        $addressMock = $this->mock(Address::class);
        $addressMock
            ->shouldReceive('whereId')
            ->with($this->_address->id)
            ->once();
            ->andReturn(null);

        //First try to go to route with non existing address
        $this->action('GET', 'AddressController@edit', ['id' => $this->_address->id]);
        $this->assertResponseStatus(404);
    }
}

Ошибка, которую он продолжает выдавать:

1) AddressControllerTest::testEdit404
Mockery\Exception\InvalidCountException: Method where("kvk", object(Mockery\Matcher\Any)) from Mockery_1_Genta_Models_Company should be called exactly 1 times but called 0 times.

Возможно, у кого-нибудь есть идея?

1 ответ

Хорошо, после нахождения нескольких постов Джеффри Уэй (парень из Laracasts) рекомендует людям прекратить насмехаться над объектами Eloquent и вместо этого использовать в базах данных памяти, я попробовал этот подход. Я подумал, что это может быть очень полезно для других пользователей, имеющих такие же проблемы в будущем, поэтому я объясню ниже.

Прямо сейчас я отредактировал мой файл 'config / database.php' для поддержки опции базы данных в памяти, используя sqlite:

'sqlite' => [
            'driver'   => 'sqlite',
            'database' => ':memory:',
            'prefix'   => '',
        ],

Далее в верхней части файла вы увидите следующую конфигурацию:

'default' => env('DB_CONNECTION', 'mysql'),

Это может остаться прежним, это означает, что Laravel проверит ваши переменные.env, чтобы найти 'DB_CONNECTION', или использует mysql по умолчанию. Это, вероятно, то, что вы хотели бы сделать, когда ваше приложение работает как обычно. Однако при тестировании вы хотели бы переопределить эту конфигурацию и временно установить конфигурацию базы данных на "sqlite". Это можно сделать, добавив переменную DB_CONNECTION в ваш файл.env:

DB_CONNECTION=mysql 

Наконец, в вашем phpunit.xml, файле конфигурации, который используется Laravel для создания модульных тестов, вы должны сказать, что при тестировании эта переменная должна быть установлена ​​в "sqlite":

<env name="DB_CONNECTION" value="sqlite"/> 

Теперь все готово, и Laravel будет запускать невидимую базу данных в памяти каждый раз, когда вы собираетесь пройти тестирование. Не забудьте добавить следующую строку в тесты, которым нужна база данных.

use \Illuminate\Foundation\Testing\DatabaseMigrations;

Он скажет Laravel запустить миграцию вашей базы данных перед началом тестов, чтобы вы могли использовать таблицы, как обычно.

Таким образом, это прекрасно работает для меня! Надеюсь, вы, ребята, можете использовать это.

Другие вопросы по тегам