php - Configure and Test Laravel Task Scheduling


Question: 

Environment

  • Laravel Version : 5.1.45 (LTS)

  • PHP Version : 5.6.1


Description

I'm trying to run a command every 1 minute using Laravel Task Scheduling.


Attempt

I've added this line to my cron tab file

* * * * * php artisan schedule:run >> /dev/null 2>&1

Here is my /app/Console/Kernel.php

<?php

namespace App\Console;

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

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

    /**
     * Define the application's command schedule.
     *
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        $schedule->command('inspire')->hourly();
        $schedule->command('echo "Happy New Year!" ')->everyMinute(); //<---- ADD HERE        }
}

I've added this line $schedule->command('echo "Happy New Year!" ')->everyMinute();


Question

How do I test this ?

How do I trigger my echo to display ?

How do I know if what I did is not wrong ?


I'm opening to any suggestions at this moment.

Any hints / suggestions / helps on this be will be much appreciated !




3 Answers: 

command() runs an artisan command. What you're trying to achieve - issuing a command to the OS - is done by exec('echo "Happy New Year!"')

Testing depends on what you want to test:

  • Whether the scheduler (every minute) is working?

In this case, you don't have to. It is tested in the original framework code.

  • Whether the command succeeds?

Well, you can manually run php artisan schedule:run and see the output.

The scheduler does not produce any output on default (>> /dev/null 2>&1). You can, however, redirect the output of the runned scripts to any file by chaining writeOutputTo() or appendOutputTo() (https://laravel.com/docs/5.1/scheduling#task-output).


For more complex logic, write a console command instead (https://laravel.com/docs/5.1/artisan#writing-commands) and use command() - this way you can write nice, testable code.

 

If you want to unit test the scheduling of events you can use this example. It is based on the default inspire command:

public function testIsAvailableInTheScheduler()
{
    /** @var \Illuminate\Console\Scheduling\Schedule $schedule */
    $schedule = app()->make(\Illuminate\Console\Scheduling\Schedule::class);

    $events = collect($schedule->events())->filter(function (\Illuminate\Console\Scheduling\Event $event) {
        return stripos($event->command, 'YourCommandHere');
    });

    if ($events->count() == 0) {
        $this->fail('No events found');
    }

    $events->each(function (\Illuminate\Console\Scheduling\Event $event) {
        // This example is for hourly commands.
        $this->assertEquals('0 * * * * *', $event->expression);
    });
}
 

Building on Michiel's answer, I've used the methods contained in Illuminate\Console\Scheduling\Event to test if the event is due to run for a given date.

I've mocked the current date using Carbon::setTestNow() so that any date based logic in the when() and skip() filters will behave as expected.

use Tests\TestCase;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Console\Scheduling\Event;

use Cron\CronExpression;
use Carbon\Carbon;


class ScheduleTest extends TestCase {


    public function testCompanyFeedbackSchedule()
    {
        $event = $this->getCommandEvent('your-command-signature');

        $test_date = Carbon::now()->startOfDay()->addHours(8);

        for ($i=0; $i < 356; $i++) { 
            $test_date->addDay();
            Carbon::setTestNow($test_date);

            // Run the when() & skip() filters
            $filters_pass = $event->filtersPass($this->app);
            // Test that the Cron expression passes
            $date_passes = $this->isEventDue($event);
            $will_run = $filters_pass && $date_passes;

            // Should only run on first friday of month
            if ($test_date->format('l') === 'Friday' && $test_date->weekOfMonth === 1) {
                $this->assertTrue($will_run, 'Task should run on '. $test_date->toDateTimeString());
            } else {
                $this->assertFalse($will_run, 'Task should not run on '. $test_date->toDateTimeString());
            }
        }
    }


    /**
     * Get the event matching the given command signature from the scheduler
     * 
     * @param  string  $command_signature
     * 
     * @return Illuminate\Console\Scheduling\Event
     */
    private function getCommandEvent($command_signature)
    {
        $schedule = app()->make(Schedule::class);

        $event = collect($schedule->events())->first(function (Event $event) use ($command_signature) {
            return stripos($event->command, $command_signature);
        });

        if (!$event) {
            $this->fail('Event for '. $command_signature .' not found');
        }

        return $event;
    }


    /**
     * Determine if the Cron expression passes.
     * 
     * Copied from the protected method Illuminate\Console\Scheduling\Event@isEventDue
     * 
     * @return bool
     */
    private function isEventDue(Event $event)
    {
        $date = Carbon::now();

        if ($event->timezone) {
            $date->setTimezone($event->timezone);
        }

        return CronExpression::factory($event->expression)->isDue($date->toDateTimeString());
    }
}
 

More Articles


php - cURL error: Could not resolve host

A similar question has been posted at but i could not find the solution thereCurl error Could not resolve host: saved_report.xml; No data record of requested type"<?php$url="http://en.wikipedia.org/wiki/Pakistan"; $ch = curl_init(urlencode($url)); echo $ch; // used to spoof that coming fro

php - curl_multi_exec kills whole process when there ia a single timeout

I am using curl_multi to send out emails out in a rolling curl script similar to this one but i added a curlopt_timeout of 10 seconds and a curlopt_connecttimeout of 20 secondshttp://www.onlineaspect.com/2009/01/26/how-to-use-curl_multi-without-blocking/while testing it i reduced the timeouts to 1ms

apache - PHP cURL timeout being ignored?

I have a node server listening on server1 and a PHP script that curls the node server from server2. This triggers a script. The problem I'm having is that I get "The connection has timed out, The server XXXXXX is taking too long to respond". The script takes a while to run as it is deploying a new s


PHP curl "Couldn't resolve host"

I'm connecting to a public API in order to sync some content between two sites. PHP CURL is used for accessing the JSON API and for downloading binary images (the code example below does not cover binary download for simplicity reasons!). Everything works fine, until after several hundred requests,

php send curl request and wait for the response

I am sending a curl request to a server that needs a few seconds to process the request and spit out a response. I believe my php script is continuing on and not waiting, therefore my foreach loop based on the response is spitting out 0 results. How can i wait for the curl transaction to complete

python - Celeryd multi with supervisord

Trying to run supervisord (3.2.2) with celery multi.Seems to be that supervisord can't handle it. Single celery worker works fine.This is my supervisord configurationcelery multi v3.1.20 (Cipater)> Starting nodes... > celery1@parzee-dev-app-sfo1: OKStale pidfile exists. Removing it. >


django - How to avoid SECRET_KEY error when starting Celery using Supervisor?

I have a Django 1.62 application running on Debian 7.8 with Nginx 1.2.1 as my proxy server and Gunicorn 19.1.1 as my application server. I've installed Celery 3.1.7 and RabbitMQ 2.8.4 to handle asynchronous tasks. I'm trying to manage my various applications, particularly Celery, using Supervisor

python - Supervisor Directory not propagated to Celery program

I have a Django application (named pcycler) that I need running with Celery. I am attempting to deploy this in production with Supervisor. I get an error when I run: supervisorctl start celeryWhich gives me: celery: ERROR (abnormal termination)Here is my celery.conf file, which is included in the

php - Cant start laravel 5.6 on Ubuntu server with NGINX

I try to install Laravel 5.6 on my production server but I have this error : [error] 12364#12364: *6 FastCGI sent in stderr: "PHP message: PHP Warning: require(/var/www/laravel/public/../vendor/autoload.php): failed to open stream: No such file or directory in /var/www/laravel/public/index.php

networking - WebSockets (JS,Apache,PHP)

I'm simply trying to get a WebSocket handshake down using javascript, apache, php.For clarity before we begin:Server version: Apache/2.4.7 (Ubuntu)PHP 5.5.9-1ubuntu4.14Chrome Version 47.0.2526.106 (64-bit)I have both port 8080 and 8090 open and accepting on both IPv4/IPv6{CLIENT}When you go to examp