【问题标题】:How to prevent Model events from firing using phpunit?如何防止模型事件使用 phpunit 触发?
【发布时间】:2015-12-24 06:03:43
【问题描述】:

我想阻止诸如“已创建”之类的模型事件。使用 phpunit 测试我的应用程序时“更新”等。

在 Laravel 的文档中,它说您可以使用

来阻止事件触发

$this->expectsEvents(App\Events\UserRegistered::class);

但在我的情况下,我没有课可期待。

即使我使用$this->withoutEvents(); 来阻止所有事件,也会触发 eloquent 事件。

我怎样才能防止雄辩的事件发生?

【问题讨论】:

  • 您是否尝试过使用 Mockery 包创建模拟?我认为这应该会有所帮助。
  • 不,我没有。你能说得更具体点吗?
  • 这是一个嘲弄的 repo,你应该看看它和文档github.com/padraic/mockery
  • 这并不能解决我的问题。

标签: laravel testing laravel-4 laravel-5 phpunit


【解决方案1】:

查看 Laravel Api Model::flushEventListeners() 应该“删除模型的所有事件侦听器”。

编辑

您可以在此基础上编写自定义方法:

public static function flushEventListeners()
{
    if (! isset(static::$dispatcher)) {
        return;
    }
    $instance = new static;
    foreach ($instance->getObservableEvents() as $event) {
        static::$dispatcher->forget("eloquent.{$event}: ".get_called_class());
    }
}

可能是这样的:

public static function flushEventListenersProvided($events)
{
    if (! isset(static::$dispatcher)) {
        return;
    }
    $instance = new static;
    foreach ($instance->getObservableEvents() as $event) {
        if(in_array($event, $events)){
          static::$dispatcher->forget("eloquent.{$event}: ".get_called_class());
        }
    }
}

并且可能将其作为特征添加到模型中,或者作为所有模型都将扩展的基本模型。此代码未经测试,但应该让您了解如何完成它。

【讨论】:

  • 这非常有用,但在我的情况下,我只想阻止一些模型事件。例如“保存”或“创建”等。
  • 这似乎工作正常,但我不想将此功能添加到我需要测试的所有模型中。它必须是一种直接从我的测试文件中执行的方法。
  • 我在文档中找不到任何其他内容。我建议创建一个特征并将其包含在模型中,或者为所有模型创建一个基本模型,将其放在那里并从中扩展所有模型。
【解决方案2】:

如果你真的只是想禁用模型的观察者,例如creatingcreatedupdated等,那么你可以调用模型的外观方法unsetEventDispatcher

例如,您可以将其放在测试类的setUp 方法中。而且您不必在tearDown 中重置它,因为框架会自动在setUp 中为下一次测试执行此操作。

protected function setUp(): void
{
    parent::setUp();
    App\MyModel::unsetEventDispatcher();
}

如果您希望这是临时的,请查看withoutEventDispatcher

您也可以getEventDispatcher 并将其保存到一个临时变量,然后在您完成业务后再次setEventDispatcher,如此答案所示:https://stackoverflow.com/a/51301753/4233593

【讨论】:

    【解决方案3】:

    方法一:

    您可以通过模拟观察者来阻止模型事件触发。方法如下:

    public function my_sexy_test()
        {
            $this->app->bind(MyModelObserver::class, function () {
                return $this->getMockBuilder(MyModelObserver::class)->disableOriginalConstructor()->getMock();
            });
    
            factory(MyModel::class)->states('pending')->create([
                    'user_id' => 1,
                    'publisher_revenue' => 10,
            ]);
    
     // Your test logic goes here
    }
    

    方法二:

    您可以在 setUp() 方法中调用以下内容:

     MyModel::unsetEventDispatcher();
    

    在 Laravel 5.6 中测试和工作。

    【讨论】:

      【解决方案4】:

      查看Model 类的shouldReceive 方法。基本上Model 类扩展了Mockery。 这是一个假设你有一个类Car的例子:

      Car::shouldReceive('save')->once();
      

      这不会命中数据库,而是使用模拟。你可以在这里找到更多关于 Laravel 测试的信息:http://laravel.com/docs/4.2/testing

      希望这会有所帮助。

      【讨论】:

      • 这并不能回答您最初的问题,而是更多的选择
      【解决方案5】:

      你应该可以使用

      $model = new App\Model($inputs);
      
      $this->expectsEvents(['eloquent.creating: App\Model', 'eloquent.saved: App\Model']);
      
      $model->save();
      

      【讨论】:

      • 试过这个解决方案了吗?它返回一个异常EventTest::testExample Exception: The following events were not fired: [eloquent.creating: App\Model] 我也尝试过不同的事件。
      • 我已经使用 Log 来跟踪所有触发的事件,而 'eloquent.creating: App\Model' 是触发的事件之一。我不明白为什么嘲弄不能抓住它。
      • 经过大量测试后,我发现这永远不会奏效,即使嘲弄不会触发错误。
      • 有谁知道为什么这不起作用?我会说应该,但这里有什么替代方案?
      猜你喜欢
      • 2021-02-14
      • 2010-10-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多