Проблема Издевательства над чертой загрузочной модели Laravel


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

Пример репозитория для приведенного ниже, с командами для воспроизведения.

В качестве примера, вот характеристика:

namespace App;
trait MyTrait
{
    public static function bootMyTrait()
    {
        print("Booting MyTrait\n");
    }
}

И модель, использующая его:

namespace App;
use Illuminate\Database\Eloquent\Model;
class MyModel extends Model
{
    use MyTrait;
}

Создание экземпляра модели регулярно работает нормально. Это показывает желаемый результат:

$model = new MyModel();

Однако попытка высмеять эту модель не помогает. Это:

use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;


    class ExampleTest extends TestCase
    {
        /**
         * A basic functional test example.
         *
         * @return void
         */
        public function testTraitBooting()
        {
            $model = $this->getMock('App\MyModel');
        }
    }


Fails. Adding some debugging to Eloquent:


    /**
     * Boot all of the bootable traits on the model.
     *
     * @return void
     */
    protected static function bootTraits()
    {
        $class = static::class;

        foreach (class_uses_recursive($class) as $trait) {
            print("\nTesting that class: $class has method: " . $method = 'boot'.class_basename($trait) . " because of Trait: $trait\n");
            if (method_exists($class, $method = 'boot'.class_basename($trait))) {
                print("Class: $class has method: $method \n");
                try {
                    forward_static_call([$class, $method]);
                } catch (\PHPUnit_Framework_MockObject_BadMethodCallException $e) {
                    print("Class: $class failed calling $method\n");
                    throw $e;
                }
            }
        }
    }

Дает нам этот сбой:

PHPUnit 5.1.0 by Sebastian Bergmann and contributors.

E                                                                   1 / 1 (100%)
Testing that class: Mock_MyModel_9ee820db has method: bootMyTrait because of Trait: App\MyTrait
Class: Mock_MyModel_9ee820db has method: bootMyTrait
Class: Mock_MyModel_9ee820db failed calling bootMyTrait


Time: 129 ms, Memory: 18.00Mb

There was 1 error:

1) ExampleTest::testTraitBooting
PHPUnit_Framework_MockObject_BadMethodCallException:

mock-bootable-laravel-model-trait/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:326
mock-bootable-laravel-model-trait/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:309
mock-bootable-laravel-model-trait/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:296
mock-bootable-laravel-model-trait/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:277
mock-bootable-laravel-model-trait/tests/ExampleTest.php:16

Я также пробовал создавать макет несколькими различными способами. Используя databasesoftdeletingtraittest в качестве примера:

use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Mockery as m;

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     *
     * @return void
     */
    public function testTraitBooting()
    {
        $mock = m::mock('App\MyModel');
        $mock->shouldReceive('bootMyTrait')->once();
    }
}

Но здесь bootMyTrait никогда не вызывается:

PHPUnit 5.1.0 by Sebastian Bergmann and contributors.

E                                                                   1 / 1 (100%)

Time: 149 ms, Memory: 19.25Mb

There was 1 error:

1) ExampleTest::testTraitBooting
Mockery\Exception\InvalidCountException: Method bootMyTrait() from Mockery_0_App_MyModel should be called
 exactly 1 times but called 0 times.

mock-bootable-laravel-model-trait/vendor/mockery/mockery/library/Mockery/CountValidator/Exact.php:37
mock-bootable-laravel-model-trait/vendor/mockery/mockery/library/Mockery/Expectation.php:271
mock-bootable-laravel-model-trait/vendor/mockery/mockery/library/Mockery/ExpectationDirector.php:120
mock-bootable-laravel-model-trait/vendor/mockery/mockery/library/Mockery/Container.php:297
mock-bootable-laravel-model-trait/vendor/mockery/mockery/library/Mockery/Container.php:282
mock-bootable-laravel-model-trait/vendor/mockery/mockery/library/Mockery.php:142
mock-bootable-laravel-model-trait/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:122

Итак, я могу двигаться код, который я выполняю в методе загрузки для поставщика услуг, но затем мне нужно будет зарегистрировать каждую модель, использующую эту особенность. Это кажется грязным, и использование метода загрузки кажется подходящим. Поэтому я думаю, что я либо попал в ошибку, либо неправильно высмеиваю модель с использованием признаков. Я посмотрел на getMockForTrait, но мне также нужен издевательский экземпляр, чтобы расширить Красноречивый (некоторые методы этой черты называют красноречивыми методами)

Если кто-нибудь увидит что-то, что я пропустил (или если я совершенно неправильно подхожу к этому), весьма признателен

Author: timbroder, 2016-04-12

1 answers

После некоторых тестов я считаю, что такого метода будет достаточно, чтобы проверить его:

$mock = m::mock('App\MyModel')->makePartial();
$mock->shouldReceive('bootMyTrait')->once();
$mock->__construct();

Объяснение:

  1. $mock = m::mock('App\MyModel')->makePartial();

    Мы создаем макет, но делаем его частичным, потому что хотим использовать конструктор класса по умолчанию и другие методы. Делая его частичным, это означает, что все методы, которые мы не переопределяем, будут использоваться из исходного класса App\MyModel

  2. $mock->shouldReceive('bootMyTrait')->once();

    Это должно быть очевидно - мы хотим проверить, выполняется ли метод bootMyTrait ровно 1 время

  3. $mock->__construct();

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

 2
Author: Marcin Nabiałek, 2016-04-21 13:30:57