0

I have the following test cases:

protected function setUp(): void
{
    parent::setUp();
    Queue::fake();
}

public function testUpdate(): void
{
    $this->updateModel(["foo" => 123]);
    $this->assertDatabaseHas("models", ["foo" => 123]);
}

public function testQueueJob(): void
{
    $this->updateModel(["bar" => 456]);
    $this->assertDatabaseHas("models", ["bar" => 456]);
    $this->assertPushed(MyJob::class);
}

public function testDontQueueJob(): void
{
    $this->updateModel(["baz" => 789]);
    $this->assertDatabaseHas("models", ["baz" => 789]);
    $this->assertNotPushed(MyJob::class);
}

The updateModel just pushes out a post request to the controller. The request is handled by this method:

public method update(Request $request, Model $model): JsonResponse
{
    $model->update($request->all());
    $model->save();
    if ($this->wasChanged("bar")) {
        MyJob::dispatch($model);
    }
    return response()->json($model);
}

So obviously, what I'm testing is that the job doesn't get pushed onto the queue. However, my final test is failing with:

  The unexpected [App\Jobs\MyJob] job was pushed.
  Failed asserting that actual size 1 matches expected size 0.

I have confirmed with dump() statements that the job is not being pushed during the third test. By swapping the second and third tests, the test suite passes successfully, suggesting that the job pushed onto the queue in the second test has persisted to the third test.

I am using the database queue driver (QUEUE_CONNECTION=database in .env.testing) and the RefreshDatabase trait is imported into my test class. This should erase the database between tests but it seems that it is not.

When I try clearing the queue manually, I get:

Call to undefined method Illuminate\Support\Testing\Fakes\QueueFake::clear()

Is there some way to clear the queue in the setUp method? Is there a different way I should be handling this?

miken32
  • 42,008
  • 16
  • 111
  • 154
  • Depending on the amount of tests it might not be practical, but removing `Queue::fake();` from the `setUp` function and putting it in each test should solve that – simplyray Jun 10 '21 at 08:17
  • Just gave it a try, added to all individual test methods. Still the same failure. – miken32 Jun 10 '21 at 15:35

2 Answers2

-1

I think you should use faker

public function testDontQueueJob(): void
{
    $newNumber = $this->faker->numberBetween(100,999);
    $this->updateModel(["baz" => $newNumber]);
    $this->assertDatabaseHas("models", ["baz" => $newNumber]);
    $this->assertNotPushed(MyJob::class);
}

to be sure that this conditional is working

if ($this->wasChanged("bar")) {
    MyJob::dispatch($model);
}

Order of tests shouldn't affect the result

Also I noticed, that you use key "baz" in test and "bar" in the controller, is it correct?

vladkash
  • 14
  • 4
  • Using a different value will have no effect on the problem I’m having. This is clearly a reduced test case of the problem. – miken32 Jun 09 '21 at 04:10
-1

Since that (important) part is missing, lets make sure you're doing both:

[...] and the RefreshDatabase trait is imported into my test class.

Are you only importing it or also using it? Your TestClass needs to look something like this:

<?php

[..]

use Illuminate\Foundation\Testing\RefreshDatabase;

[..]

class YourTest extends TestCase
{
    use RefreshDatabase;

    [..]
   
}
simplyray
  • 1,200
  • 1
  • 16
  • 25
  • 1
    Yes, when one talks about importing a trait, the `use` statement in the class is what does that. The `use` statement before the class is not importing anything, it's aliasing. https://www.php.net/manual/en/language.namespaces.importing.php – miken32 Jun 09 '21 at 14:24
  • true that! sorry, didn't look at your reputation and tried to avoid doing beginners mistakes ... – simplyray Jun 10 '21 at 08:22