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

Viewed 3276

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), 
        ];
    }
}
4 Answers

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.

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

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