Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

trevorpan's avatar

How do you set up a truth test console command to run a task in production?

Recently, I made a post and was successfully able to write a test for this condition: https://laracasts.com/discuss/channels/testing/the-expected-notification-was-not-sent-using-cron-scheduled-command-phpunit

However, now that it's in production the scheduler does not run when a job deadline passes. I've tried with --force and without.

The docs mention a routes/console.php is needed for a closure. This truth test appears to be a closure, however in the docs https://laravel.com/docs/7.x/scheduling#schedule-frequency-options under Truth Test Constraints it makes no mention of the routes/console.php file???

class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        Commands\DeadlineHasPassed::class,
        Commands\GenerateSitemap::class
    ];

    /**
     * Define the application's command schedule.
     *
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
       $schedule->command('email:deadlineHasPassed --force')
            ->everyMinute()->when(function () {
            DB::table('jobs')
                ->where([
                    ['deadline', '<=', Carbon::now('America/Los_Angeles')
                        ->toDateTimeString()],
                    ['bidded', '=', 0]
                ])->exists();
        })->withoutOverlapping();
0 likes
7 replies
bugsysha's avatar

I've never used this approach, but are you missing a return in your callback?

1 like
trevorpan's avatar

@bugsysha yea, I had originally used the return. Here's the doc sample:

$schedule->command('emails:send')->daily()->when(function () {
    return true;
});

So my code was like:

protected function schedule(Schedule $schedule)
    {
       $schedule->command('email:deadlineHasPassed --force')
    ->everyMinute()
        ->when(function () {
            DB::table('jobs')
                ->where([
                    ['deadline', '<=', Carbon::now('America/Los_Angeles')
                        ->toDateTimeString()],
                    ['bidded', '=', 0]
                ])->get();
        })->withoutOverlapping();

return true;

I looked at that for a bit. It seemed like get() was the wrong call whereas exists() returns true or false. So it seems redundant to check for a truth and then explicitly return true as this could falsely return true.

Am I wrong?

bugsysha's avatar
bugsysha
Best Answer
Level 61

I was referring to return within callback for when.

$schedule->command('email:deadlineHasPassed --force')
            ->everyMinute()->when(function () {
            return DB::table('jobs') // notice here return
                ->where([
                    ['deadline', '<=', Carbon::now('America/Los_Angeles')
                        ->toDateTimeString()],
                    ['bidded', '=', 0]
                ])->exists();
        })->withoutOverlapping();
trevorpan's avatar

aahh. Ok let me give that a go.

I had tried this but it did not work either.

        $schedule->command('email:deadlineHasPassed --force')
            ->everyMinute()
                ->withoutOverlapping()
                    ->when(function () {
                       $biddedJobs = DB::table('jobs')
                            ->where([
                                ['deadline', '<=', Carbon::now('America/Los_Angeles')->toDateTimeString()],
                                ['bidded', '=', 0]
                            ])
                            ->exists();
                        return $biddedJobs;
                    });
bugsysha's avatar

Do you get anything when you run that query?

trevorpan's avatar

Alright, your solution should have run but it has not. There are no logs @bugsysha .

class DeadlineHasPassed extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'email:deadlineHasPassed {biddedJobs}'; // should this be model name?

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Send deadline expired emails';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     * @param SlugJob $biddedJobs
     */
    public function handle(SlugJob $biddedJobs)
    {
        SendDeadlineExpiredEmails::dispatch($biddedJobs);
    }
}

Well, this has to be it. If the truth test tests whether a condition exists does the console command require arguments? If so, would the user be the argument or the model SlugJob? I've tried both. Something seems to be amiss.

I also verified the php artisan queue:restart is set in envoyer...

trevorpan's avatar

@bugsysha thank you so much. I was at a complete loss as to why this was not going. Your return statement above was the most important. But I realized how badly everything else was setup.

Truth Test Steps:

  1. Make sure statement returns true or false, as shown above
  2. Depending on whether the console command sends emails make sure constructors are set properly, and args implemented
  3. If dispatching a huge number of things you may not need an arg as shown here:
    /**
    * Execute the console command.
    *
    */
    public function handle()
    {
    SendDeadlineExpiredEmails::dispatch();
    }

The above was really confusing, as the docs imply the use of args. But my case is a bit different as it loops through many jobs to email a bunch of users based on other conditions...

  1. In my case the queue job did not need type hints, as the handle() pulled special conditions and it was not an implied model like making a blog post or something.
  2. Make sure envoyer, or other deployment uses php artisan queue:restart
  3. AND most important be sure to include php artisan schedule:run

Wow, thank you. This was a really complicated setup.

Got the expected real emails on production server!!:

Your job has bidded: George Washington's Lumber Job (sample) We are very sorry to report that no one placed a bid on your job.

Please or to participate in this conversation.