3

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), 
        ];
    }
}
5
  • 2
    You should extend the TestCase from Laravel, this TestCase also extends the PHPUnit\Framework\TestCase, so you will have Laravel methods & PhpUnit methods. Try Commented Nov 27, 2020 at 11:29
  • 1
    Run composer update to make sure everything's up to date? If this project was created in an older version of Laravel, make sure your composer.json has been updated to match the latest one, which uses a different faker library. Commented Nov 27, 2020 at 15:59
  • 1
    I can use the same code in my factory without problem (although I don't; I just let the DB autoincrement take care of primary keys for me.) Is there a reason your question spends so much time on the testing aspect? Does the factory work in other contexts? Commented Nov 27, 2020 at 16:00
  • @miken32 what command did you use? php artisan test ? Commented Nov 30, 2020 at 9:04
  • 1
    I was just testing the factory code alone, but it looks like it was a wider issue for you. Commented Nov 30, 2020 at 15:52

5 Answers 5

11

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.

Sign up to request clarification or add additional context in comments.

3 Comments

I'm using a Unit Test. Even when extending PHPUnit\Framework\TestCase , I can't use factories in data providers.
@JarsOfJam-Scheduler I see what you mean now exactly. It will work, if you call $this->refreshApplication(); inside the data provider. But you'd still need to extend the Tests\TestCase.
It makes a lot of sense because you can do a pure unit test in isolation from the framework, thank you for this explanation.
2

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.

1 Comment

I wonder how are you then supposed to put objects in data providers without typing them all out by hand.
1
  1. 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)

  2. Second common issue (as mentioned above) is the class that you extend your Test with - see above

  3. 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();
    }

Comments

0

I had the same issue and this is how I solved it on Laravel 11: in pest.php add 'unit' in the pest function, and make it something like this:

pest()->extend(Tests\TestCase::class)
    ->use(Illuminate\Foundation\Testing\RefreshDatabase::class)
    ->in('Feature', 'Unit');

Comments

-2

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;
     
     // ...
 }

3 Comments

It doesn't work, same problem :) I'm going to try to extend from Laravel. (use Illuminate\Foundation\Testing\TestCase; )
Edit: Illuminate\Foundation\Testing\TestCase makes me to implement an abstract method. It's definitely different from what I've read in the official documentation (link given in the OP)
Btw laravel.com/docs/8.x/database-testing#writing-factories says there isn't need for using WithFaker

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.