如何防止模型事件使用 phpunit 触发?

Posted

技术标签:

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

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

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

来阻止事件触发

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

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

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

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

【问题讨论】:

您是否尝试过使用 Mockery 包创建模拟?我认为这应该会有所帮助。 不,我没有。你能说得更具体点吗? 这是一个嘲弄的 repo,你应该看看它和文档github.com/padraic/mockery 这并不能解决我的问题。 【参考方案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://***.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' 是触发的事件之一。我不明白为什么嘲弄不能抓住它。 经过大量测试后,我发现这永远不会奏效,即使嘲弄不会触发错误。 有谁知道为什么这不起作用?我会说应该,但这里有什么替代方案?

以上是关于如何防止模型事件使用 phpunit 触发?的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 4 模型事件不适用于 PHPUnit

如何防止默认事件触发但仍允许事件冒泡

单击子锚点时,如何防止触发父级的 onclick 事件?

IE 10, 11. 如何防止使用占位符的文本输入触发焦点输入事件?

如何防止ajax调用两次触发

从输入中删除字符时,如何防止 keyup 事件触发两次?