【问题标题】:PHPUnit how to write a Laravel Nova Observer testPHPUnit 如何编写一个 Laravel Nova Observer 测试
【发布时间】:2021-09-14 12:49:41
【问题描述】:

我想为我的 CommentObserver 编写一个测试。这个观察者只在 NovaServiceProvider 中注册,在 AppServiceProvider 中没有。这意味着我无法使用我自己的控制器来测试我的观察者。

在我看来,我有 3 种方法来测试我的观察者:

  1. 通过向 Nova API 发送发布请求来执行功能测试
  2. 通过调用观察者中的函数来模拟观察者以检查该函数是否按预期执行
  3. 尝试在 AppServiceProvider 中动态注册我的观察者,执行请求并再次在 AppServiceProvider 中取消注册观察者。

我试图为这 3 种方法中的任何一种找到解决方案来测试我的观察者,但不幸的是我没有用其中任何一种方法。

问题:

  • 对于方式 1,我总是收到验证错误,Nova 告诉我输入无效。
  • 对于方式 2,我无法模拟观察者函数
  • 对于方式 3,我没有找到有关如何在 AppServiceProvider 上即时注册和注销 oberserver 的任何解决方案

你们有关于如何测试我的 CommentObserver 的想法和解决方案(上面写的只在我的 NovaServiceProvider 中注册)。


更新: 所以,这是我的观察者的代码。我需要有一个有效的请求来测试我的观察者,以便能够访问$request->input('images') 变量。我知道我也可以使用$comment->content 而不是request()->input('content'),因为$comment->content 已经包含了新的内容,此时没有保存。

我需要有效请求的原因是变量images 不是Comment 模型的一部分。所以我不能使用$comment->images,因为它根本不存在。这就是为什么我需要访问请求输入。我的观察者所做的基本上是从内容中提取base64 图像,将它们保存到服务器并用图像链接替换它们。

class CommentObserver
{
    public function updating(Comment $comment)
    {
        if (!request()->input('content')) {
            return;
        }

        if (request()->input('content') == $comment->getRawOriginal('content')) {
            return;
        }

        $images = request()->input('images');
        if(!is_array($images)) {
            $images = json_decode(request()->input('images'));
        }

        checkExistingImagesAndDeleteWhenNotFound($comment, request()->input('content'), 'comments', 'medium');
        $comment->content = addBase64ImagesToModelFromContent($comment, request()->input('content'), $images, 'comments', 'medium');
    }
}

这是我目前的测试。我选择了方式 1,但如前所述,这总是会导致 nova 控制器出现验证错误,我无法弄清楚错误是什么/缺少什么或错误。

class CommentObserverTest extends TestCase
{
    /** @test */
    public function it_test()
    {
        $user = User::factory()->create([
            'role_id' => Role::getIdByName('admin')
        ]);

        $product = Product::factory()->create();

        $comment = Comment::factory()->create(['user_id' => $user->id, 'content' => '<p>Das ist wirklich ein super Preis!</p>', 'commentable_type' => 'App\Models\Product', 'commentable_id' => $product->id]);

        $data = [
            'content' => '<p>Das ist wirklich ein HAMMER Preis!</p>',
            'contentDraftId' => '278350e2-1b6b-4009-b4a5-05b92aedaae6',
            'pageStatus' => PageStatus::getIdByStatus('publish'),
            'pageStatus_trashed' => false,
            'commentable' => $product->id,
            'commentable_type' => 'App\Models\Product',
            'commentable_trashed' => false,
            'user' => $user->id,
            'user_trashed' => false,
            '_method' => 'PUT',
            '_retrieved_at' => now()
        ];

        $this->actingAs($user);

        $response = $this->put('http://nova.mywebsiteproject.test/nova-api/comments/' . $comment->id, $data);

        dd($response->decodeResponseJson());

        $das = new CommentObserver();
    }
}

诚挚的问候和感谢

【问题讨论】:

  • 对于您在编写第一个测试时遇到的三个问题中的每一个,您认为哪些测试最简单,同时对您想了解的代码最有效(1 , 2 或 3)?对于那个问题,您更愿意采取什么行动,或者您可以做什么?
  • 嗨@hakre 我认为最简单的方法是编写一个 Laravel Nova 功能测试并执行一个正常的 Laravel Nova 发布请求,所以第 1 号。但是,这总是会导致验证错误。如果我选择方法 2 或 3,我也必须伪造一个请求,因为我的 updating 方法需要访问它。
  • @Jan 我会说选项 2,除了不嘲笑观察者本身。只测试观察者的功能,没有别的,模拟任何依赖
  • @duncan 我如何伪造 HTTP 请求?因为我的观察者正在访问请求中的输入,所以我需要伪造它。
  • 将您的代码添加到您的问题中,包括测试和观察者

标签: php laravel unit-testing testing phpunit


【解决方案1】:

为什么要依赖NovaServiceProvider 中的引导方法?可以在测试中即时调用 observe() 方法:

class ExampleTest extends TestCase
{    
    /** @test */
    public function observe_test()
    {
        Model::observe(ModelObserver::class);


        // If you need the request helper, you can add input like so:
        request()->merge([
           'content' => 'test'
        ]);

        // Fire model event by updating model
        $model->update([
            'someField' => 'someValue',
        ]);

        // Updating should be triggered in ModelObserver
    }
}

现在应该可以在您的观察者类中使用:

public function updating(Model $model)
{
    dd(request()->input('content')); // returns 'test'
}

【讨论】:

  • 是的,这是可能的。但是我需要伪造一个 HTTP 请求,因为我的观察者需要一个 HTTP 请求才能工作。你知道这是怎么做到的吗?
  • 您不需要伪造 HTTP 请求,因为您正在使用帮助程序。您可以简单地将字段“合并”到请求中。我已经用一个代码示例更新了我的初始评论。我已经在本地对此进行了测试,它应该可以工作!如果您有任何问题,请告诉我。
  • @Jan 上面的实现你试过了吗?
  • 谢谢你的队友@Eric Landheer。这是工作!我花了一些时间来测试它,因为我不在家。对不起!
猜你喜欢
  • 2019-03-06
  • 2019-06-10
  • 2017-10-19
  • 2017-11-10
  • 2020-06-22
  • 2013-06-12
  • 1970-01-01
  • 2014-09-05
  • 2017-11-29
相关资源
最近更新 更多