lolsokje's avatar

LazyLoadingViolationException when running one specific test, on a relationship that's never called

For context, I'm working on a RNG-based motorsport simulator, for lack of a better term. Users can create universes (think FIA in real life terms), in a universe they can create series (think F1, F2, F3 etc) and in each series they can create seasons.

I'm currently working on improving test coverage but I'm now getting a LazyLoadingViolationException that I can't explain, since I only get the exception in my test and not when browsing normally.

The test;

/** @test */
public function theIndexPageShowsAllSeasonsForTheSelectedSeries()
{
    $user = User::factory()->create();
    $series = $this->createSeriesForUser($user);
    Season::factory(5)->for($series)->create();

    $this->actingAs($user)
        ->get(route('series.seasons.index', [$series]))
        ->assertOk()
        ->assertInertia(fn(Assert $page) => $page
            ->component('Seasons/Index')
            ->has('series.seasons', 5)
        );
}

createSeriesForUser() (inside Laravel's TestCase class);

protected function createSeriesForUser(User $user): Series
{
    return Series::factory()->for(Universe::factory()->for($user)->create())->create();
}

The route is a resource controller for the Season model, and calls the index method;

public function index(Series $series): Response
{
    return Inertia::render('Seasons/Index', [
        'series' => $series->load(['seasons' => fn(HasMany $query) => $query->orderBy('year')]),
        'can' => [
            'edit' => Gate::check('owns-universe', $series->universe),
        ],
    ]);
}

The exact exception is Illuminate\Database\LazyLoadingViolationException: Attempted to lazy load [series] on model [App\Models\Season] but lazy loading is disabled., which tells me, somewhere, the series method is called on a Season object, which doesn't happen anywhere in the code that's actually called.

Adding

protected $with = [
    'series',
];

to the Season model fixes the exception, which makes no sense to me since in the entire application, the series relationship on the Season model is called only in tests, and unrelated Vue components.

I've got two questions regarding this issue;

  1. why am I only getting this error when running this specific test (other tests and visiting the page normally are fine)
  2. what's causing the exception, when at no point the relationship is called?
0 likes
5 replies
mabdullahsari's avatar

It's probably because of this line:

Season::factory(5)->for($series)->create()

You can try setting the relation manually to see if it's still thrown.

Season::factory(5)->create()->each->setRelation('series', $series);

If it works, just disable lazy loading protection in tests.

lolsokje's avatar
lolsokje
OP
Best Answer
Level 17

@mabdullahsari Nah it was just me being stupid, I completely forgot I was appending two attributes for serialisation on the Season model, both of which called $this->series and were used in $appends. Commenting those out made the test pass, so I'll just have to keep using $with = ['series']; to satisfy the preventLazyLoading.

mabdullahsari's avatar

@lolsokje You'll have to be careful with that, because it is going to eager load the relation whenever you query this model even if you don't need it.

lolsokje's avatar

@mabdullahsari I am aware, but in this case it's not a problem since I pretty much always use at least one of the appended attributes in my components, so the relationship would've loaded either way.

Please or to participate in this conversation.