Summer Sale! All accounts are 50% off this week.

rossiluca's avatar

Notification::fake() throwing TypeError: Illegal offset type

Hi all, I'm writing a test that test an endpoint. In this endpoint I send an email notification.

When I add

Notification::fake()

The test fail with the following error:

TypeError: Illegal offset type

Anyone has any hint?

0 likes
19 replies
Sinnbeck's avatar

Can you show the complete class? Are you sure you are importing the facade?

rossiluca's avatar

@Sinnbeck I'm using Pest and importing the facade

use Illuminate\Support\Facades\Notification;
rossiluca's avatar

@Sinnbeck

Here:

TypeError: Illegal offset type in /path-to-my-project/vendor/laravel/framework/src/Illuminate/Support/Testing/Fakes/NotificationFake.php:329
Stack trace:
#0 /path-to-my-project/vendor/laravel/framework/src/Illuminate/Support/Testing/Fakes/NotificationFake.php(294): Illuminate\Support\Testing\Fakes\NotificationFake->sendNow(Array, Object(App\Notifications\MyNotification))
rossiluca's avatar

@Sinnbeck This is the test

test('why my test is not working', function () {
    $user = User::factory()->make();

    Notification::fake();

    login()
        ->post('/api/myendpoint/export')
        ->assertStatus(201)
        ->assertJson(['message' => 'Export sent successfully', 'status' => 'success']);


    Notification::assertSentTo(
        $user, MyNotification::class
    );
});

The notification is a brand-new standard Notification.

rossiluca's avatar

@Sinnbeck This is the controller function that send the notification

public function __invoke(Request $request)
    {
        $request->user()->notify(new MyNotification());
        
        return response()->json([
            'message' => 'Export sent successfully',
            'status' => 'success',
        ], 201);
    }

And this is the notification

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class MyNotification extends Notification
{
    use Queueable;

    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Get the notification's delivery channels.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['mail'];
    }

    /**
     * Get the mail representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    public function toMail($notifiable)
    {
        return (new MailMessage)
                    ->line("This is the email body");
    }

    /**
     * Get the array representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function toArray($notifiable)
    {
        return [
            //
        ];
    }
}

Sinnbeck's avatar

@rossiluca hmm cannot see the error. Did you change the user model so it returns some strange ID?

Can you try

dd($request->user()->getKey());
rossiluca's avatar

@Sinnbeck I'm using uuid so this is the response

Ramsey\Uuid\Lazy\LazyUuidFromString {#1774 // app/Http/Controllers/API/MyController.php:14
  -unwrapped: null
  -uuid: "19af3b3c-dca5-4947-9910-2ae1842bebf6"
  uuid: "19af3b3c-dca5-4947-9910-2ae1842bebf6"
}
Sinnbeck's avatar

@rossiluca Oh interesting. That might very well be the issue here. It gets a class, not a string back. Can you show the user model ?

Sinnbeck's avatar

Btw. You can try recreating the issue yourself.

$foo['19af3b3c-dca5-4947-9910-2ae1842bebf6'] = 'bar';
$bar = $foo[$request->user()->getKey()];
rossiluca's avatar

@Sinnbeck Sure, not much inside right, now. Only the boot method to creare the uuid

namespace App\Models;

use App\Traits\UuidTrait;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Str;
use Laravel\Sanctum\HasApiTokens;
use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable, UuidTrait, HasRoles;

    public $incrementing = false;

    protected $keyType = 'string';

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'first_name',
        'last_name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    public static function boot()
    {
        parent::boot();

        static::creating(function ($issue) {
            $issue->id = Str::uuid(36);
        });
    }
}
Sinnbeck's avatar
Sinnbeck
Best Answer
Level 102

@rossiluca Try casting it

        static::creating(function ($issue) {
            $issue->id = (string) Str::uuid(36);
        });
1 like
rossiluca's avatar

@sinnbeck Nicee, this works! Thanks a lot! Lost half of a day trying to resolve the issue 😅

Thank you very much

owellcarneiro's avatar

@rossiluca I had this problem today (Laravel 10). I'm also using uuids as unique keys and using the Trait Illuminate\Database\Eloquent\Concerns\HasUuids; on the models. As the uuid is generated automatically by the trait, to fix the problem, I inserted:

protected $casts = [
     'id'  => 'string',
]; 

Hope that helps someone in the future

Please or to participate in this conversation.