Unable to locate factory in Laravel 7 - laravel

I am making a unit test for PHPunit. The code is:
<?php
namespace Tests\Unit;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use PHPUnit\Framework\TestCase;
class ThreadTest extends TestCase
{
use DatabaseMigrations;
public function test_a_thread_has_replies()
{
$thread = factory('App\Thread')->create();
$this->assertInstanceOf('Illuminate\Database\Eloquent\Collection', $thread->replies);
}
}
When I run PHPUnit i get the error:
InvalidArgumentException: Unable to locate factory for [App\Thread].
I have a threadfactory:
<?php
/** #var \Illuminate\Database\Eloquent\Factory $factory */
use App\Thread;
use Faker\Generator as Faker;
$factory->define(Thread::class, function (Faker $faker) {
return [
'user_id' => factory(App\User::class),
'title' => $faker->sentence,
'body' => $faker->paragraph
];
});
If I use the factory in Tinker it works fine.
I have similar testfunctions and factories for other classes and these work fine.
Most likely I am making a very stupid error, but I cannot find it.
Any suggestions?
Kind regards,
HUbert

You are extending the php unit testcase class. Which does not load your Laravel application. You should extends the use Tests\TestCase provided by Laravel.
Using php artisan make:test UserTest on the CLI will create a test with the correct template fitting for testing a Laravel application.

What worked for me was to use
$factory->define(App\Model::class, function (Faker $faker) {
//return code here
}
Instead of just using the Model name without placing APP\ in front of the model name
$factory->define(Model::class, function (Faker $faker) {
//return code here
}

Related

Lumen - Using factory() function in tests make error - Undefined Function

I'm using Lumen 8.3 ,wanted to use factory() function in my tests, it gives me
Undefined Function ,there is nothing useful in the Docs of Lumen
Am i missing something here?
class ProductTest extends TestCase
{
public function test_if_can_send_products_list(){
$products = factory('App/Products',5)->make();
$this->json('post','/payouts',$products)
->seeJson([
'created' => true,
]);
}
}
->
Error: Call to undefined function factory()
It's better to use direct class like that:
$products = factory(Products::class, 5)->create();
don't forget to add Products model usage (namespace).
Edit
You should create Factory:
<?php
namespace Database\Factories;
use App\Products;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
class ProductFactory extends Factory
{
protected $model = Products::class;
public function definition(): array
{
return [
'name' => $this->faker->unique()->userName()
];
}
}
And add HasFactory Trait to your model:
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Products extends Model {
use HasFactory;
}
you can also use it like this
Products::factory()->count(5)->make();
I just uncommented these lines in app.php file
$app->withFacades();
$app->withEloquent();
Apparently Laravel 8 removed the 'factory' helper, and it seems Lumen followed that path without updating documentation;
#Faesal Answer is the correct way to do it these days;
remember to add use HasFactory; to your Model.

Laravel 8 Tests: PHPUnit error: Unknown formatter "unique"

I have written a test that involves a factory. When I execute the test, I get this error:
The data provider specified for Tests\Unit\ExampleTest::testTakePlace is invalid. InvalidArgumentException: Unknown formatter "unique" /var/www/html/api/vendor/fakerphp/faker/src/Faker/Generator.php:249
Expected result
This error should not be shown, I should be able to use $this->faker->unique().
How I tried to fix this problem
By reading the doc again and again (no difference was found) and by reading questions&answers on the Internet (only one question & only one answer were found: to extend Laravel's TestCase but the official documentation, as I mentionned it, says the contrary). (Laravel's TestCase is provided by use Illuminate\Foundation\Testing\TestCase;)
Question
Why doesn't it work and how to fix this bug?
Sources
Test sources
It extends PHPUnit\Framework\TestCase (not Laravel's TestCase) because the documentation says to extend it. Indeed: https://laravel.com/docs/8.x/testing#creating-and-running-tests . This is not the only part of the doc mentionning to extend it.
<?php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
use App\Models\Project;
class ExampleTest extends TestCase
{
/**
* #dataProvider provideTakePlaceData
*/
public function testTakePlace($project)
{
$response = $this->json('GET', '/controllerUserProject_takePlace', [
'project_id' => $project->id
]);
}
public function provideTakePlaceData() {
return [
Project::factory()->make()
];
}
}
Controller
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class ControllerUserProject extends Controller
{
public function takePlace(Request $request, $project_id)
{
return;
}
}
The most important: the factory
<?php
namespace Database\Factories;
use App\Models\Project;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
class ProjectFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* #var string
*/
protected $model = Project::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'id' => $this->faker->unique()->numberBetween(1, 9000),
];
}
}
Change:
use PHPUnit\Framework\TestCase;
to:
use Tests\TestCase;
Why?
When your ExampleTest extends PHPUnit\Framework\TestCase Laravel app is never initialized in tests, and so you don't have access to Laravel features like factories.
The documentation tells you to extend PHPUnit\Framework\TestCase;, but it refers to Unit tests. Feature tests should extend Tests\TestCase. This is something pretty new. Until Laravel 5.8 both unit and feature tests were extending Tests\TestCase by default. I personally just define all tests as feature tests to avoid such issues.
The problem was due to the use of a dataProvider with the use of the factories. More precisely: PHPUnit data providers should not be used when Laravel factories are.
If you are using faker in Factories please make sure these are working correctly regardless (as per example try running Project::factory()->make(); within Laravel Tinker and see the results)
Second common issue (as mentioned above) is the class that you extend your Test with - see above
Third and frequently omitted one that's causing this error, is a missing parent constructor call in the setUp() - (if you use it)
<?php
namespace Tests\Unit;
use Tests\TestCase;
use App\Models\Project;
class ExampleTest extends TestCase
{
protected Project $project;
public function setUp(): void
{
parent::setUp();
$this->project = Project::factory()->make();
}
I think you need to use the WithFaker trait to use faker :
<?php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
use App\Models\Project;
use Illuminate\Foundation\Testing\WithFaker;
class ExampleTest extends TestCase
{
use WithFaker;
// ...
}

Test Case with laravel Dusk showing error 'users_email_unique'

I'm getting error as on below screenshot
Please help me i am trying and for this i have spend too much time but still issue no luck.
I am using laravel official dusk package for front-web testing. When i am running login test case its showing error "users_email_unique" which is above showing in picture. I am using use DatabaseTransactions but it is not reverting my last transaction. For this i am using test database also. Here is my my code:
<?php
namespace Tests\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
use App\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class LoginTest extends DuskTestCase
{
use DatabaseTransactions;
/**
* A Dusk test example.
*
* #return void
*/
public function test_I_can_login_successfully()
{
$user = factory(User::class)->create([
'email' => 'login#gmail.com',
'password' => bcrypt('password'),
]);
$this->browse(function ($browser) use ($user) {
$browser->visit('/')
->type('email', 'login#gmail.com')
->type('password', 'password')
->press('Login')
->assertSee($user->name);
});
}
}
For Dusk you cannot use use DatabaseTransactions. You need to use DatabaseMigrations instead. When you update trait then you should get rid of the error.
You have sample Dusk test in documentation and it looks like this:
<?php
namespace Tests\Browser;
use App\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Chrome;
use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase
{
use DatabaseMigrations;
/**
* A basic browser test example.
*
* #return void
*/
public function testBasicExample()
{
$user = factory(User::class)->create([
'email' => 'taylor#laravel.com',
]);
$this->browse(function ($browser) use ($user) {
$browser->visit('/login')
->type('email', $user->email)
->type('password', 'password')
->press('Login')
->assertPathIs('/home');
});
}
}
As you see it's also using DatabaseMigrations trait.

Build a Laravel App With TDD - Episode 4, InvalidArgumentException: Unable to locate factory with name [default] [App\Project]

I am deep in bug town here, can anyone help?
I am following the tutorial line by line and have got as far as the unit test in the episode:
https://laracasts.com/series/build-a-laravel-app-with-tdd/episodes/4
I get the error:
1) Tests\Unit\ProjectTest::test_it_has_a_path
InvalidArgumentException: Unable to locate factory with name [default] [App\Project].
Some info:
The unit test is the second test in the video (starts # 4:18), my
code works fine for the first (feature) test, and the factory
function works fine in tinker.
I have tried all kinds of different filepaths for 'App\Project', and
also tried static Project::class type references.
My phpunit didn't run tests unless I prefixed the word 'test', even
when filtering to the exact test name, so I believe this may be due
to an underlying phpunit version difference.
My unit test:
namespace Tests\Unit;
use Illuminate\Foundation\Testing\RefreshDatabase;
use PHPUnit\Framework\TestCase;
class ProjectTest extends TestCase
{
use RefreshDatabase;
public function test_it_has_a_path()
{
$project = factory('App\Project')->create();
$this->assertEquals('/project/' . $project->id, $this->path());
}
}
My Project factory
/** #var \Illuminate\Database\Eloquent\Factory $factory */
use App\Project;
use Faker\Generator as Faker;
$factory->define(Project::class, function (Faker $faker) {
return [
'title' => $faker->sentence,
'description' => $faker->sentence
];
});
My show method
public function show(Project $project)
{
return view('projects.show', compact('project'));
}
My route
Route::get('projects/{project}', 'ProjectsController#show');
and the working feature test (for reference)
public function test_a_user_can_view_a_project()
{
$this->withoutExceptionHandling();
$project = factory('App\Project')->create();
$this->get("/projects/" . $project->id)
->assertSee($project->title)
->assertSee($project->description);
}
I got this answer on the Laracasts forum that solved my problem:
the reason is because the unit test now extends from the PHPUnit testcase class instead of the framework testcase..
So you should avoid using factory in Unit tests. This was a change recently..
To be able to still use the factory, you should import this TestCase.
use Tests\TestCase;
And remove this:
use PHPUnit\Framework\TestCase;
And here is the change:
https://github.com/laravel/framework/commit/e30a0c979d98f2f1f7b6c565e4002734237a280b

Laravel - How to use faker in PHPUnit test?

It's giving me this error when I run the test:
undefined variable $faker.
This is the WithFaker file.
https://github.com/laravel/framework/blob/5.5/src/Illuminate/Foundation/Testing/WithFaker.php
<?php
namespace Tests\Unit;
use App\User;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
class LoginTest extends TestCase
{
use WithFaker;
/**
* A basic test example.
*
* #return void
*/
/** #test */
public function test_example()
{
$user = User::create([
'username' => $faker->firstName(),
]);
}
}
You have to use $this->faker->firstName() not just $faker->firstName()
Update 1
Now when we use WithFaker Trait $this->faker will give us null, to get around this make sure to call $this->setupFaker() first.
e.g.
class SomeFactory
{
use WithFaker;
public function __construct()
{
$this->setUpFaker();
}
}
credit #Ebi
For anyone coming here from 2021. We no longer require the addition of
$this->setUpFaker();
You only need to include the trait as described in the accepted answer.
once you completed installation of Faker.
include autoload file and create instance
$faker = \Faker\Factory::create();
$faker->firstname()
$faker->lastname()
For more information visit
check you seed function run (Faker $faker).

Resources