Skip to main content

Command Palette

Search for a command to run...

Laravel: Testing, Factory, and Events

Disable events while testing

Updated
2 min read
Laravel: Testing, Factory, and Events

I was testing my Laravel application. I usually write factories to create fake data on-fly while testing. While I was writing factory, I came across this error.

SQLSTATE[23000]: Integrity constraint violation: 19 NOT NULL constraint failed: post_statuses.user_id (SQL: insert into "post_statuses" ("status", "post_id", "user_id", "updated_at", "created_at") values (DRAFT, 1, ?, 2022-01-22 19:41:55, 2022-01-22 19:41:55))

Explaining the error: The field user_id is not nullable in the database, but the value passed was null. So the database threw an error stating that user_id can't be null.

I could not get my head around why user_id is null as I knew I passed the id as you can see from the code snippet.

// creating user
$user = User::factory()->create();

// Create post and status
PostStatusFactory::factory(5)
  ->for(Post::factory())
  ->forUser($user)
  ->create();

I even tried to hard code the data.

// creating user
$user = User::factory()->create();

// creating Post
$post = Post::factory()->create();

// creating PostStatus
PostStatus::factory()->create([
  'post_id' => 1,
  'user_id' => 1
]);

But, I was getting the same error.

Then, I remembered that I used eloquent event creating on PostStatus model. I used this event to add the logged-in user's id to the model.

creating event is dispatched before the model is created, which can modify or add new data to the model.

class PostStatus extends Model
{
   // ...other codes 

   protected static function booted()
    {
        static::creating(function($postStatus){
            $postStatus->user_id = Auth::id();
        });
    }
}

Events are dispatched even when the models are created using factories. As I was in the testing environment and had no authenticated user, thus Auth::id() gave me null.

Solving the error

Now I knew that the error would be solved if the events were not dispatched, so I disabled the event temporarily by using withoutEvents method.

// creating user
$user = User::factory()->create();

// Create post and status without event
PostStatus::withoutEvents(function() ($user) {
  PostStatusFactory::factory(5)
    ->for(Post::factory())
    ->forUser($user)
    ->create();
});

Here, I temporarily disabled a single event on this occasion as I required them on other tests, but you can disable the events by using WithoutEvents traits.

use Illuminate\Foundation\Testing\WithoutEvents;

class PostTest extends TestCase
{
  use WithoutEvents;
  // other codes ...
}

Voilà! Problem solved. Finally, there was ✔️ ✔️ ✔️ in my test.