想测试 laravel 中工厂创建的特定方法的调用

Posted

技术标签:

【中文标题】想测试 laravel 中工厂创建的特定方法的调用【英文标题】:Want to test the calling of a specific method created by a factory in laravel 【发布时间】:2021-11-04 06:09:04 【问题描述】:

我正在尝试编写一个执行类似操作的集成测试。

使用ModelClass::factory([$params])而不是构造函数在测试的setUp方法中创建类的实例

在这个类上调用一些导致各种事情发生的方法

事件会在某个时候被触发

监听事件,事件监听器将调用产生事件的同一对象的方法。

具体调用哪个方法(和参数)取决于类内部的一些逻辑(这主要是我要测试的)。

方法本身没有做任何事情(目前),但我想知道它被调用了。功能可能会在未来某个时候由其他人实现。

所以我想断言这个方法在测试过程中的某个时间点被调用,并测试哪些参数被传递给它。目前我已经设法模拟出事件本身以检查它是否被触发,但我只能测试该方法是否被手动调用(通过将dd() 放入其中)。如果不模拟整个对象,我似乎无法找到一种方法来做到这一点(这会破坏测试的目的,因为那时我只是在测试模拟)。

我知道你可以做“partial”模拟,也许这就是我需要的,但这似乎不适用于::factory([$params]) 构造函数风格。

我将此作为答案包含在内,因为我无法编辑我的原件。

<?php
    // The test itself

    protected function setUp() : void
    
        parent::setUp();
        $this->item = Item::factory()->createOne([
            "name" => "testing name",
            // Some other attributes
        ]);
    

    public function test_the_thing() : void
    
        $this->item->doSomeWork();
        $this->assertShouldHaveBeenCalled($item, "someMethod") // I want something like this
    

    // Then a trait attached to the item class that looks like this

    public static function bootItemTrait(): void
    
        Event::listen(function(MyCustomEvent $event) 
            if(method_exists($event->item,"someMethod") && some_other_conditions()) 
                $event->item->someMethod();
            
        );
    

    public function doSomeWork() : void 
        $result = doSomeComplicatedThings()
        MyCustomEvent::dispatch($this,$result);
    

    // My custom event is just a basic event with this constructor

    public function __construct($item, $result)
    
        $this->result = $result;
        $this->item = $item;
    

【问题讨论】:

您需要包含一些样板代码或制作的示例,以便我们有一些代码来重现您的问题。毫无疑问这是可能的:) 我刚刚添加了一些示例代码,它们是我正在尝试做的简化版本。 【参考方案1】:

欢迎来到嘲讽的欢乐世界,愿我做你的向导。您想模拟调用并从那里获取,问题是依赖注入和模拟Laravel 模型并不容易,不推荐。

为了解决这个问题,请为您的调用创建一个包装器类,这将允许您覆盖 Laravel 容器中的包装器。所以你在哪里-&gt;doWork();。你可以做类似的事情。重要的是要知道,在使用依赖注入时,永远不要使用关键字new。而是使用app(YourClass::class);resolve(YourClass::class); 或使用构造函数/句柄注入。在这种情况下,我使用resolve() 方法。

class YourModel extends Model 

    public function doWork()
    
        /** @var DoWorkService $doWorkService */
        $doWorkService = resolve(DoWorkService::class);

        $doWorkService->doWork(1, "2");
    

在这个构思的例子中,我做了一个空壳来证明这一点。

namespace App\Services;

class DoWorkService

    public function doWork($param1, $param2) 
        throw new \Exception('This is mocked and should not be called');
    

现在在您的测试课程中,您可以使用 Laravel 提供的 Mocking 助手。此示例需要调用一次doWork() 方法,参数为1, "2",在这种情况下返回true

namespace Tests\Unit;

use App\Services\DoWorkService;
use Mockery\MockInterface;
use Tests\TestCase;

class DoWorkTest extends TestCase

    public function testDoWork()
    
        $yourModel = // create your model with the factory;

        $mock = $this->mock(DoWorkService::class, function (MockInterface $mock) 
            $mock->shouldReceive('doWork')
                ->once()
                ->with(1, "2")
                ->andReturn(true);
        );

        $yourModel->doWork();
    

这是在我的本地项目上测试的,所以你应该设置为实现类似的东西。

【讨论】:

以上是关于想测试 laravel 中工厂创建的特定方法的调用的主要内容,如果未能解决你的问题,请参考以下文章

Laravel - 向工厂创建的模型添加关系

Laravel 模型工厂没有连接到数据库

如何测试 Laravel 5.4 中的“创建”事件是不是调用了 Eloquent 模型方法?

测试是否在Laravel中调用webhook路由

在laravel 5.2中使用工厂关系违反完整性约束

Laravel PHPUnit 测试运行产生异常 调度器创建后无法设置默认工厂