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

tobyreed's avatar

Testing - Wait for event to finish before assertion

Hello,

One of my tests is failing due to timing. If I add a 5 second delay it will ALWAYS pass. I figured it out and narrowed it down to it being an event being "fired" and the assertion happening fast.

I was wondering if there is a way to wait for the event to finish & then make the assertion.

I currently have queue set to sync, if anyone is wondering :)

Thanks!

0 likes
14 replies
Epitome's avatar

Hi.

Did you chain the response status with the assertion? eg.

$response ->assertStatus(201) ->assertJson([ 'created' => true, ]);

A bit more information would help. (Code example)

Sinnbeck's avatar

Can you show the test and how you fire the event?

tobyreed's avatar

@epitome @sinnbeck Hi, yes of course silly me !

public function an_editor_can_update_a_revision()
    {
        $this->putJson(route('processes.locks', ['process' => $this->process]), ['is_locked' => true])
            ->assertSuccessful();

        $this->assertDatabaseHas(ProcessLock::getTableName(), [
            'process_id' => $this->process->id,
            'user_id' => $this->user->id
        ]);

        $this->postJson(route('processes.revisions.store', ['process' => $this->process]), [
            'xml' => $xml = $this->faker->paragraph,
        ])->assertOk();

        $this->assertDatabaseHas(Xml::getTableName(),[
            'content' => $xml
        ]);

        /** @var Xml $xmlId */
        $xml = Xml::whereContent($xml)->firstOrFail();
        
        $this->assertDatabaseHas(Revision::getTableName(),
            [
                'process_id' => $this->process->id,
                'created_by' => $this->user->id,
                'status_id' => Status::whereName(Revision::getDefaultStatus())->first(['id'])->id,
                'xml_id' => $xml->id
            ]);
    }

The issue is at the very bottom on the database assertion! :(

tobyreed's avatar

yes of course!

if (isset($validatedData['xml']) && $process->latest_revision_id == $revision->id) {
            event(new UpdatingRevisionXml($revision, $validatedData['xml']));
        }

This is how it's fired.

This is the event code:

    public function __construct(Revision $revision, $xml = null, $time = null)
    {
        parent::__construct();

        $this->revision = $revision;
        $this->xml = $xml;
        $this->time = $time ? $time : now();
   }
Sinnbeck's avatar

So the event does nothing? Or does it call an api or something?

tobyreed's avatar

Well, the event is fired when when xml is sent to one of the endpoints: processes.revisions.store.

But the test will fail as the XML has been inserted into the table id. But the Revision tables relation has not been updated to the new xml_id even when it has been stored.

Sinnbeck's avatar

Do you mean that the event shouldn't be run? I don't see any saving in it. If so you can add this to the start of the test

Event::fake();
tobyreed's avatar

No, I want the event to run.

The issue is that XML is saved to the db, but when I come to do the db assertion at the very end for the Revision table the xml_id has not been updated on the revision table (yet) but if I put a sleep for 5 seconds in the test before this, it will pass.

It's very inconsistent and the only thing it could be is the event for UpdatingRevisionXml as it's run as a job (even though the QUEUE_DRIVER is set to sync)

Epitome's avatar

Could it depend on the http response? Try:

$this->postJson(route('processes.revisions.store', ['process' => $this->process]), [
            'xml' => $xml = $this->faker->paragraph,
        ])->assertStatus(201);
tobyreed's avatar

If by depend you mean that the http response that's posted there is the one that should update the Revision.xml_id then you would be correct. But the issue is it returns a 201 and it's successful etc. But the assertion SOMETIMES fails for the db revision table

Epitome's avatar

assertOk() checks for 200, while 201 would contain http response 'created'. Tbh, I'm not quite sure, if that is the issue, but I think it is worth a shot. (I'm still learning too. ;-))

tobyreed's avatar

Yeah you're correct with the status codes. I am just unsure why it will sometimes fail

sgilberg's avatar

I know this is an old thread, but I was coming across something similar and it seemed to be related to the use of database transactions. Once I added 'after_commit' => true to the 'sync' connection in the queue.php config my tests started consistently passing, where they were previously flaky. Hope this helps someone.

Please or to participate in this conversation.