Is Me\MyPackage\Models\Product; correct or is it src/Models/Product.php
Error: Model not found when using factories in package tests
I'm receiving this error when running tests in a Laravel 8 package I'm developing:
Error: Class 'App\Product' not found
/package/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Factories/Factory.php:625
/package/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Factories/Factory.php:345
/package/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/GuardsAttributes.php:157
/package/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Factories/Factory.php:350
/package/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Factories/Factory.php:318
/package/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Factories/Factory.php:230
/package/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Factories/Factory.php:227
/package/tests/Unit/ProductTest.php:30
It seems somewhere along the line the Factory is using the wrong namespace to try and use the Product model.
Here is the Factory:
// database/factories/ProductFactory.php
<?php
namespace Me\MyPackage\Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Me\MyPackage\Models\Product;
class ProductFactory extends Factory
{
/**
* The name of the factory's corresponding product.
*
* @var string
*/
protected $product = Product::class;
/**
* Define the product's default state.
*
* @return array
*/
public function definition()
{
return [
'name' => $this->faker->words(3, true)
];
}
}
And my Model looks like so:
// src/Models/Product.php
<?php
namespace Me\MyPackage\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Product extends Model
{
use HasFactory;
protected $guarded = [];
protected static function newFactory()
{
return \Me\MyPackage\Database\Factories\ProductFactory::new();
}
}
My test looks like so:
// tests/Unit/ProductTest.php
<?php
namespace Me\MyPackage\Tests\Unit;
use Me\MyPackage\Models\Product;
use Me\MyPackage\Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
class ProductTest extends TestCase
{
use RefreshDatabase;
/** @test */
function a_product_has_a_name()
{
$product = Product::factory()->create([
'name' => 'Fake name',
]);
$this->assertEquals('Fake name', $product->name);
}
}
Here is my TestCase:
// tests/TestCase.php
<?php
namespace Me\MyPackage\Tests;
use Me\MyPackage\MyPackageServiceProvider;
class TestCase extends \Orchestra\Testbench\TestCase
{
public function setUp(): void
{
parent::setUp();
// additional setup
}
protected function getPackageProviders($app)
{
return [
MyPackageServiceProvider::class,
];
}
protected function getEnvironmentSetUp($app)
{
include_once __DIR__ . '/../database/migrations/create_products_table.php.stub';
(new \CreateProductsTable)->up();
}
}
And finally here's my composer.json file:
{
"name": "me/my-package",
"description": "A package for Laravel",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Me",
"email": "[email protected]"
}
],
"require": {
"orchestra/testbench": "^6.13"
},
"autoload": {
"psr-4": {
"Me\MyPackage\": "src",
"Me\MyPackage\Database\Factories\": "database/factories",
"Me\MyPackage\Tests\": "tests"
}
},
"require-dev": {
"phpunit/phpunit": "^9.5"
},
"extra": {
"laravel": {
"providers": [
"Me\MyPackage\MyPackageServiceProvider"
]
}
},
"scripts": {
"test": "vendor/bin/phpunit",
"test-f": "vendor/bin/phpunit --filter"
}
}
And I have definitely run composer dump-autoload multiple times as I've been trying various things.
Often with things like this it turns out to be a simple namespacing issue, but I'm at the point now where I've checked all the namespaces and gone through the whole thing with a fine tooth comb, and still the error persists.
Digging a bit deeper into the trace, in vendor/laravel/framework/src/Illuminate/Database/Eloquent/Factories/Factory.php the modelName() method is returning App\Product rather than Me\MyPackage\Models\Product so something's breaking down along the way.
// vendor/laravel/framework/src/Illuminate/Database/Eloquent/Factories/Factory.php:633
public function modelName()
{
$resolver = static::$modelNameResolver ?: function (self $factory) {
$factoryBasename = Str::replaceLast('Factory', '', class_basename($factory));
$appNamespace = static::appNamespace();
return class_exists($appNamespace.'Models\'.$factoryBasename)
? $appNamespace.'Models\'.$factoryBasename
: $appNamespace.$factoryBasename;
};
dd($this->model ?: $resolver($this)); // Debugging added by me
return $this->model ?: $resolver($this);
}
Do I have to inject something to tell appNamespace to return my package namespace when resolving this?
Any ideas or help would be much appreciated, as I say I've been through this numerous times now and can't find anyone else online having the same issue, and as far as I can see I have everything set up correctly.
Thanks in advance
Please or to participate in this conversation.