如何在 Laravel 中模拟 Job 对象?
How to mock a Job object in Laravel?
当谈到 Laravel 中的队列测试时,我使用提供的 Queue Fake 功能。但是,在某些情况下,我需要为 Job class.
创建一个 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');
那个代码returns:Error: Call to undefined method App\Jobs\ProcessRunEndpoint::shouldReceive()
。
我还尝试使用 $this->instance()
将作业实例与模拟对象交换,但效果不佳。
也就是说,我如何测试 catch 块中的代码?
根据 docs,您应该能够通过为队列提供的模拟来测试作业。
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()
只是 returns 一个对象,onQueue()
只是一个 setter。那里没有其他逻辑。相反,我们可以通过搞乱配置来让一切都失败。
而不是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');
}
}
我找到了解决办法。我没有使用 facade 将作业添加到队列 (App\Jobs\ProcessRunEndpoint::dispatch($apiRequest)->onQueue('run');
),而是将其注入到控制器的操作中:
public function store(ProcessRunEndpoint $processRunEndpoint)
{
// try-catch block in case the Redis server is down
try {
$processRunEndpoint::dispatch($apiRequest)->onQueue('run');
} catch (\Exception $e) {
//handle the case when the job is not pushed to the queue
}
}
有了这个,作业对象从容器中解析出来,所以它可以被模拟:
$this->mock(ProcessRunEndpoint::class, function ($mock) {
$mock->shouldReceive('dispatch')
->once()
->andThrow(new \Exception());
});
尽管仍然不确定为什么 shouldReceive
方法对外观不起作用:https://laravel.com/docs/8.x/mocking#mocking-facades
当谈到 Laravel 中的队列测试时,我使用提供的 Queue Fake 功能。但是,在某些情况下,我需要为 Job class.
创建一个 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');
那个代码returns:Error: Call to undefined method App\Jobs\ProcessRunEndpoint::shouldReceive()
。
我还尝试使用 $this->instance()
将作业实例与模拟对象交换,但效果不佳。
也就是说,我如何测试 catch 块中的代码?
根据 docs,您应该能够通过为队列提供的模拟来测试作业。
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()
只是 returns 一个对象,onQueue()
只是一个 setter。那里没有其他逻辑。相反,我们可以通过搞乱配置来让一切都失败。
而不是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');
}
}
我找到了解决办法。我没有使用 facade 将作业添加到队列 (App\Jobs\ProcessRunEndpoint::dispatch($apiRequest)->onQueue('run');
),而是将其注入到控制器的操作中:
public function store(ProcessRunEndpoint $processRunEndpoint)
{
// try-catch block in case the Redis server is down
try {
$processRunEndpoint::dispatch($apiRequest)->onQueue('run');
} catch (\Exception $e) {
//handle the case when the job is not pushed to the queue
}
}
有了这个,作业对象从容器中解析出来,所以它可以被模拟:
$this->mock(ProcessRunEndpoint::class, function ($mock) {
$mock->shouldReceive('dispatch')
->once()
->andThrow(new \Exception());
});
尽管仍然不确定为什么 shouldReceive
方法对外观不起作用:https://laravel.com/docs/8.x/mocking#mocking-facades