Understand Laravel Event::fake() with (Model Events)
Tags: laravel, testing, fake
Published: 2020-02-21 15:53:13

If you are a laravel developer who cares about writing well-tested software then you might have seen this problem before or at least you are going to face it soon,

In my case, It was a task that allows our users to create widgets for their websites, each widget has a configuration with the total number of ads and the total number of recommended articles.

So, I have a `Widget` model and `WidgetConfiguration` model. every time I want to create a new widget I would create a configuration row for it. I used Laravel model events to do so.

```php
class Widget extends Model
{
    public static function boot()
    {
        parent::boot();

        static::created(function($model){
            dispatch(new CreateWidgetConfiguration($model));
       });
     }
}
```

```php
// WidgetsController

public function store(WidgetStoreRequest $request){   
    Widget::create($request->all());
    dispatch(new NewWidgetCreated($model));
}
```

Every time I create a widget it will fire `CreateWidgetConfiguration` event and there is another event called `NewWidgetCreated` event that will send an email to account managers.

When It comes to writing tests, and you want to make sure every time you create a new widget it must create a configuration for it but you don’t want to fire a `NewWidgetCreated` event because you don’t want to send an email or notification every time we run tests.

So let’s write the unit tests first

```php
public function test_create_widget_with_configuration()
{
    Event::fake();
    $user = factory(User::class)->create();

    $response = $this->actingAs($user)
    ->json("POST","/api/widgets",[
        'name' => 'My First Widget',
        'domain' => 'https://my-first-widget.com',
    ]);
    $this->assertDatabaseHas('widgets_configuration',[
        'widget_id' => 1
    ]);
}
```

the previous test will always fail because Event::fake() won’t fire the `CreateWidgetConfiguration` event.

Let’s take a look at the content of the fake method

```php
public static function fake($eventsToFake = [])
{
    static::swap($fake = new EventFake(static::getFacadeRoot(), $eventsToFake));

    Model::setEventDispatcher($fake);
}
```

It says that it will fake events and replace the event dispatcher of the model to be the `EventFake` class.

In order to solve this issue and only fake the global Event class without model dispatcher, we have to set Model dispatcher manually after calling the event fake. so the final result will be something like

```php
$initialEvent = Event::getFacadeRoot();
Event::fake();
Model::setEventDispatcher($initialEvent);
```

and the final test case method is

```php
public function test_create_widget_with_configuration()
{
    $initialEvent = Event::getFacadeRoot();
    Event::fake();
    Model::setEventDispatcher($initialEvent);
    $user = factory(User::class)->create();

    $response = $this->actingAs($user)
    ->json("POST","/api/widgets",[
        'name' => 'My First Widget',
        'domain' => 'https://my-first-widget.com',
    ]);
$this->assertDatabaseHas('widgets_configuration',[
        'widget_id' => 1
    ]);
}
```

This is it for now. I hope you at least have got the idea of how event fake works. The main purpose of the article was to let you know what it is and how to deal with it.

If you find any kind of misinformation here, please feel free to let me know and I will update it.