首页 > 解决方案 > 如何在 Laravel 中模拟 Job 对象?

问题描述

在 Laravel 中进行队列测试时,我使用了提供的Queue Fake功能。但是,在某些情况下,我需要为 Job 类创建一个 Mock。

我有以下代码将作业推送到 Redis 支持的队列:

  $apiRequest->storeRequestedData($requestedData); // a db model
  // try-catch block in case the Redis server is down
    try {
        App\Jobs\ProcessRunEndpoint::dispatch($apiRequest)->onQueue('run');
        $apiRequest->markJobQueued();
    } catch (\Exception $e) {
        //handle the case when the job is not pushed to the queue
    }

我需要能够测试 catch 块中的代码。因此,我试图模拟 Job 对象,以便能够创建一个会引发异常的伪造者。

我在我的单元测试中试过这个:

   ProcessRunEndpoint::shouldReceive('dispatch');

该代码返回:Error: Call to undefined method App\Jobs\ProcessRunEndpoint::shouldReceive()。我还尝试使用模拟对象交换作业实例,$this->instance()但效果不佳。

也就是说,我如何测试 catch 块中的代码?

标签: laravelunit-testingmockingphpunitjobs

解决方案


根据文档,您应该能够通过为队列提供的模拟来测试作业。

Queue::assertNothingPushed();
// $apiRequest->storeRequestedData($requestedData);
// Use assertPushedOn($queue, $job, $callback = null) to test your try catch
Queue::assertPushedOn('run', App\Jobs\ProcessRunEndpoint::class, function ($job) {
    // return true or false depending on $job to pass or fail your assertion
});

使该行App\Jobs\ProcessRunEndpoint::dispatch($apiRequest)->onQueue('run');抛出异常有点复杂。dispatch()只是返回一个对象,onQueue()只是一个二传手。那里没有其他逻辑。相反,我们可以通过弄乱配置让一切都失败。

而不是Queue::fake();,用一个不起作用的覆盖默认队列驱动程序:Queue::setDefaultDriver('this-driver-does-not-exist');这将使每个作业调度失败并抛出一个ErrorException.

极简主义示例:

Route::get('/', function () {
    try {
        // Job does nothing, the handle method is just sleep(5);
        AJob::dispatch();
        return view('noError');
    } catch (\Exception $e) {
        return view('jobError');
    }
});
namespace Tests\Feature;

use App\Jobs\AJob;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Facades\Queue;
use Tests\TestCase;

class AJobTest extends TestCase
{
    /**
     * @test
     */
    public function AJobIsDispatched()
    {
        Queue::fake();

        $response = $this->get('/');

        Queue::assertPushed(AJob::class);

        $response->assertViewIs('noError');
    }

    /**
     * @test
     */
    public function AJobIsNotDispatched()
    {
        Queue::setDefaultDriver('this-driver-does-not-exist');

        $response = $this->get('/');

        $response->assertViewIs('jobError');
    }
}

推荐阅读