<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Chisonm</title>
    <description>The latest articles on DEV Community by Chisonm (@chisonm).</description>
    <link>https://dev.to/chisonm</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F628893%2Fa7c3fde3-5baa-4f32-ba48-d5e554f1ea2b.png</url>
      <title>DEV Community: Chisonm</title>
      <link>https://dev.to/chisonm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chisonm"/>
    <language>en</language>
    <item>
      <title>I Enabled Strict Mode In Laravel 12 and My Tests Started Failing Everywhere</title>
      <dc:creator>Chisonm</dc:creator>
      <pubDate>Fri, 31 Oct 2025 09:10:50 +0000</pubDate>
      <link>https://dev.to/chisonm/i-enabled-strict-mode-in-laravel-12-and-my-tests-started-failing-everywhere-2ekh</link>
      <guid>https://dev.to/chisonm/i-enabled-strict-mode-in-laravel-12-and-my-tests-started-failing-everywhere-2ekh</guid>
      <description>&lt;p&gt;So there I was, being a good developer. Writing tests, enabling strict mode, doing all the right things. Then I run my test suite and suddenly everything is red.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MissingAttributeException: The attribute [seller_approval_status] either does not exist or was not retrieved for model [App\Models\User].
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But here's the thing - my tests were passing before. The code hasn't changed. And when I manually test the feature in my browser, it works perfectly fine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What the hell is going on?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Turns out, Laravel 12's strict mode has this fun quirk where it makes your tests explode in ways that have nothing to do with whether your actual code works. Let me explain the nightmare I just lived through so you don't have to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Feature That Sounded Amazing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Laravel 12's &lt;code&gt;Model::shouldBeStrict()&lt;/code&gt; looked like a gift from the coding gods, Don't get me wrong, this will save you time. One line of code that protects you from common mistakes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// In AppServiceProvider::boot()
Model::shouldBeStrict();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;This single line activates three safety nets:&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;preventSilentlyDiscardingAttributes()&lt;/strong&gt; - Yells at you when you try to mass-assign attributes that aren't fillable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;preventLazyLoading()&lt;/strong&gt; - Catches those sneaky N+1 queries before they murder your database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;preventAccessingMissingAttributes()&lt;/strong&gt; - Stops you from accessing attributes that don't exist.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first two? Absolutely brilliant. Game changers. The third one? Yeah, that's where things went sideways.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here's Where Everything Falls Apart&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's something I didn't know: Laravel's authentication doesn't load all your user data. I mean, it makes sense when you think about it. Why fetch 20 columns when you only need 5 for authentication? So when you call &lt;code&gt;$this-&amp;gt;actingAs($user)&lt;/code&gt;, Laravel does something like this under the hood:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// What Laravel actually does internally
$user = User::select([
    'id', 
    'email', 
    'password', 
    'remember_token', 
    'email_verified_at'
])-&amp;gt;find($user-&amp;gt;id);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what's missing? Your custom columns. All those nice fields you added for your business logic? Not there. And that's when strict mode loses its mind.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Test That Should Have Worked&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's the test I was writing nothing fancy, just checking if a user can access their dashboard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test('seller with pending approval sees pending page', function () {
    $user = User::factory()-&amp;gt;create([
        'seller_approval_status' =&amp;gt; 'pending'
    ]);

    $response = $this-&amp;gt;actingAs($user)-&amp;gt;get('/dashboard');

    $response-&amp;gt;assertOk();
    $response-&amp;gt;assertViewIs('pending');
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Here's what happened:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;seller_approval_status&lt;/code&gt; column exists in your database. It's right there.&lt;/li&gt;
&lt;li&gt;But Laravel didn't SELECT it when loading the authenticated user (to save memory)&lt;/li&gt;
&lt;li&gt;Strict mode sees you trying to access an attribute that's not loaded&lt;/li&gt;
&lt;li&gt;Strict mode panics and throws an exception&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's technically protecting you from a problem that doesn't exist. Classic overzealous bouncer situation. The Real Problem: &lt;code&gt;actingAs()&lt;/code&gt; is Broken (Sort Of)&lt;/p&gt;

&lt;p&gt;The kicker? &lt;strong&gt;The browser version works fine.&lt;/strong&gt; When real users log in, Laravel loads the user differently. So your tests fail while your actual app works. Or sometimes it's the other way around. Absolutely maddening.&lt;/p&gt;

&lt;h2&gt;
  
  
  It Gets Worse in Weird Ways
&lt;/h2&gt;

&lt;p&gt;The bugs in Laravel 12's strict mode make testing even more unpredictable:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Factory data sometimes works&lt;/strong&gt;&lt;br&gt;
If you access the attribute immediately after creating the model, it works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'role'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'admin'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ✅ Works fine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But after &lt;code&gt;actingAs()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;actingAs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 💥 Exception&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. The &lt;code&gt;:memory:&lt;/code&gt; database lie&lt;/strong&gt;&lt;br&gt;
Using SQLite's &lt;code&gt;:memory:&lt;/code&gt; database? Your tests might pass locally and fail in CI, or vice versa. The in-memory database handles default values differently than file-based databases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Some attributes work, others don't&lt;/strong&gt;&lt;br&gt;
Casted attributes sometimes bypass the check. Sometimes. I never figured out the pattern, and I don't think Laravel has either.&lt;/p&gt;

&lt;p&gt;I wasted an entire afternoon trying to figure out why &lt;code&gt;email_verified_at&lt;/code&gt; worked but &lt;code&gt;seller_approval_status&lt;/code&gt; didn't. So the problem is that the attribute literally doesn't exist on the model instance when &lt;code&gt;actingAs()&lt;/code&gt; reloads it, which is exactly why we get the exception. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So how do we fix this?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are a couple of ways to go about this, but this is what I did instead. After trying a bunch of different approaches, here's what actually worked for my test suite.&lt;/p&gt;

&lt;p&gt;Add default values to your User model for any attribute you access in tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Authenticatable&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$fillable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'seller_approval_status'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="c1"&gt;// This one line fixed all my tests&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'seller_approval_status'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when &lt;code&gt;actingAs()&lt;/code&gt; loads a partial user, these attributes exist with default values. If they were loaded from the database, the real values are used. If not, the defaults kick in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My tests went from red to green instantly.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this works for testing:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your factory still sets the real values&lt;/li&gt;
&lt;li&gt;When the test creates the user, it has the factory data&lt;/li&gt;
&lt;li&gt;When &lt;code&gt;actingAs()&lt;/code&gt; reloads the user partially, defaults fill in the gaps&lt;/li&gt;
&lt;li&gt;Your controller code accesses the attributes without exceptions&lt;/li&gt;
&lt;li&gt;Zero performance hit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The only downside:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need to identify which attributes are causing test failures (run your tests, they'll tell you)&lt;/li&gt;
&lt;li&gt;Have to pick reasonable defaults (usually &lt;code&gt;null&lt;/code&gt; is fine)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
