Laravel命令调度咨询:每月第二个周二10点执行及测试方法
疑问解答
1. when闭包是否仅在命令实际调度执行时求值?
是的。Laravel调度器依赖系统cron每分钟触发一次php artisan schedule:run,每次运行时会遍历所有任务,逐一评估包括when闭包在内的所有条件,只有全部条件满足时才会执行对应命令。
2. 是否需要保留monthly()条件?
不需要。monthly()会限制调度器仅在每月第一天评估任务条件,但你要执行的时间是每月第二个周二(绝非每月第一天),这会导致调度器在第二个周二当天直接跳过该任务的条件检查。删掉monthly()后,调度器才会每分钟检查任务条件,确保符合要求的时间触发任务。
3. 该调度是否仅在每月第二个周二10点执行?
当前代码无法实现需求,问题出在monthly()的存在——它让调度器只在每月第一天处理这个任务,第二个周二当天调度器不会检查该任务的条件。删掉monthly()后,调度器会在每个周二10点判断当前是否为当月第二周,满足条件则执行,才能精准实现每月第二个周二10点执行的效果。
修正后的代码:
$schedule->command('foo') ->tuesdays() ->at('10:00') ->when(function () { return Carbon::now()->weekOfMonth == 2; }) ->withoutOverlapping() ->appendOutputTo(storage_path($logPath));
无需实际运行命令的测试方法
方法一:用Carbon模拟日期验证条件
在临时脚本或代码中,用Carbon的setTestNow()模拟目标日期,直接验证条件是否成立:
use Carbon\Carbon; // 模拟2024年5月第二个周二(5月14日)10:00 Carbon::setTestNow(Carbon::create(2024, 5, 14, 10, 0)); var_dump(Carbon::now()->weekOfMonth == 2); // 输出bool(true) // 模拟第一个周二10点 Carbon::setTestNow(Carbon::create(2024, 5, 7, 10, 0)); var_dump(Carbon::now()->weekOfMonth == 2); // 输出bool(false) // 模拟第二个周二9点 Carbon::setTestNow(Carbon::create(2024, 5, 14, 9, 0)); // 此时周数符合但时间不符,任务不会执行
方法二:编写单元测试验证调度时机
利用Laravel自带的调度测试特性,编写单元测试验证任务执行时机:
use Carbon\Carbon; use Illuminate\Foundation\Testing\Concerns\InteractsWithSchedule; use Tests\TestCase; class FooCommandScheduleTest extends TestCase { use InteractsWithSchedule; public function test_foo_command_runs_on_second_tuesday_at_10() { // 验证第二个周二10点任务会触发 $this->travelTo(Carbon::create(2024, 5, 14, 10, 0)); $this->schedule()->command('foo')->shouldBeDue(); // 验证第一个周二10点任务不触发 $this->travelTo(Carbon::create(2024, 5, 7, 10, 0)); $this->schedule()->command('foo')->shouldNotBeDue(); // 验证第二个周二9点任务不触发 $this->travelTo(Carbon::create(2024, 5, 14, 9, 0)); $this->schedule()->command('foo')->shouldNotBeDue(); // 验证非周二的第二周10点任务不触发 $this->travelTo(Carbon::create(2024, 5, 13, 10, 0)); // 周一 $this->schedule()->command('foo')->shouldNotBeDue(); } }
运行php artisan test即可验证调度逻辑是否正确。
方法三:调度器 dry-run 模式
Laravel 8+支持--dry-run参数,执行php artisan schedule:run --dry-run会模拟调度器运行,输出待执行任务但不会实际执行。可临时在AppServiceProvider的boot方法中用Carbon::setTestNow(...)设置测试时间,再运行该命令查看输出,确认任务是否会被触发。
内容的提问来源于stack exchange,提问作者Adam Pearson

