Framework: `dispatchNow()` does not call `failed()` but `dispatch()` does.

Created on 2 Jan 2020  路  6Comments  路  Source: laravel/framework

  • Laravel Version: 6.5.0
  • PHP Version: 7.3.8
  • Database Driver & Version: n/a

Description:

I'm using MyJob::dispatchNow() in my tests (I'm since aware I can just use MyJob::dispatch() as the driver is set to sync for testing) and running into issues because MyJob::failed is not being called when the job fails (I'm testing my jobs fail nicely).

After digging through the source, it appears that MyJob::failed() is not triggered when using dispatchNow().

I'm not sure whether to report this as a bug in dispatchNow() or just in the documentation which suggests the failed method will always be called if the job fails.

Steps To Reproduce:

Here's a unit test which should illustrate the problem.

  1. Install laravel (or use an existing one)
  2. Save the test below into tests/Unit/TestMyJob.php
  3. Run phpunit tests/Unit/TestMyJob.php
<?php

namespace Tests\Unit;

use Tests\TestCase;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class MyJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public static $putStatusHere = 'pending';

    public function handle()
    {
        self::$putStatusHere = "running";
        throw new \Exception('This job fails!');
    }

    public function failed()
    {
        self::$putStatusHere = "failed";
    }
}

class MyJobTest extends TestCase
{

    public function testFailedGetsCalled()
    {
        $this->assertEquals('pending', MyJob::$putStatusHere);
        // When using dispatchNow() - the failed method does not get called.
        try {
            MyJob::dispatchNow();
        } catch (\Exception $e) {
            // So the status is still 'running' despite the error.
            $this->assertEquals('running', MyJob::$putStatusHere);
        }

        // But whyen using dispatch() the `sync` driver gets used (as per the testing
        // config) and the failed method does get called.
        try {
            MyJob::dispatch();
        } catch (\Exception $e) {
            // Ignore the exception. The point is the job failed.
            $this->assertEquals('failed', MyJob::$putStatusHere);
        }
    }
}

bug

Most helpful comment

I think we should just hint about it in the docs, no need to change the behaviour.

All 6 comments

It indeed seems weird that it doesn't works given that a dispatch in combination with the sync driver does work.

Always thought that the "failed" method was only for queued jobs. "dispatch" assumes a queued job and doesn't care which driver is used. If you're using "dispatch" in production" you should use "dispatch" in tests. DispatchNow is never for queued jobs so no need for the "failed" method.

Just my thoughts.

I can see why it's like that, but it's not obvious - so I think either the documentation should make it clear, or the function could be named more descriptively "failedOnQueue()".

I think we should just hint about it in the docs, no need to change the behaviour.

In the end I have to agree with @themsaid and @devcircus about this. Let's make the docs a bit more clear on this. Feel free to send in a PR.

Thanks all, pull request created.

Was this page helpful?
0 / 5 - 0 ratings