I just created a factory in order to run some tests:
CategoryFactory.php
<?php
namespace DatabaseFactories;
use IlluminateDatabaseEloquentFactoriesFactory;
/**
* @extends IlluminateDatabaseEloquentFactoriesFactory<AppModelsCategory>
*/
class CategoryFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
//
'company_id' => 1,
'name' => fake()->name(),
'parent_id' => null
];
}
}
And added it to the model:
Category.php
<?php
namespace AppModels;
use IlluminateDatabaseEloquentFactoriesHasFactory;
use IlluminateDatabaseEloquentModel;
use IlluminateDatabaseEloquentSoftDeletes;
use AppScopesCompanyScope;
class Category extends Model
{
//
use SoftDeletes, HasFactory;
protected $fillable = [
'company_id',
'name',
'parent_id'
];
public function __construct() {
parent::__construct();
if(isset(auth()->user()->company_id)) {
$this->attributes['company_id'] = auth()->user()->company_id;
}
}
protected static function boot()
{
parent::boot();
static::addGlobalScope(new CompanyScope);
}
My test file looks like this:
public function test_category_edit_can_be_redered(): void
{
$user = User::factory()->create();
$category = Category::factory()->create();
$response = $this->actingAs($user)->get('/devices/categories/1/edit');
$response->assertStatus(200);
}
But when I try to run the test, I get the following error:
General error: 1364 Field 'company_id' doesn't have a default value (Connection: mysql, SQL: insert into `categories` (`updated_at`, `created_at`) values (2023-08-08 01:55:43, 2023-08-08 01:55:43))
It looks like the definition in the factory is not being applied to the create method… Any idea what I am doing wrong?
2
Answers
If anyone face this issue in the future, here is the answer:
Laravel factory fails when the model has a constructor
You trigger your category before you signIn a user so that the code doesn’t have a default
auth()->user()
Your code trigger a global scope event which is overriding the default
company_id
you can use
use IlluminateDatabaseConsoleSeedsWithoutModelEvents
or saveQuietly() in your seeder to not trigger global scope events.You can also do this I think: