Search code examples
phpsymfonyfixturesfaker

How to implement your own Faker provider in Symfony


In order to reduce some code duplication in my fixture classes, I would like to create a custom provider for Faker (fakerphp/faker) in my Symfony 5 application. Where can I create a custom faker provider in my application? Please note that I am new to developing in Symfony.

Related to this topic I have found this question for Laravel, but of course Symfony has a different project setup.

Reading up to the documentation of a related library, I would expect I could use $faker->addProvider(), but then again I do not know where this code should live in my Symfony application.

I fantasize I should make a Service for this functionality, or maybe I should just add it to my BaseFixture class? But these are just wild guesses for me.


Solution

  • Where you put your provider is a subjective question. Some use src/Test/ (or namespace App\Test) for test related code that is not actual tests. Others will place it next to the tests somewhere in the tests/ directory, e.g. tests/Faker/ (with namespace App\Tests\Faker). The benefit of the latter is, that the classes will only be picked up by autoload-dev and not in production.

    Regarding the second part of your question, how to add the provider to Faker that depends on how you use it in your tests or more precisely, if you want to fetch a shared faker instance from your container or manually create one. Especially if your provide relies on other services like Doctrine you probably want to register it in the container or you should rethink your provider as relying on the database might cause issues, e.g. when you reset your test database, the data pool for your provider might be gone.

    I would go for creating a faker instance in your tests as the cost of initiating Faker is relatively low, it should not noticeably slow down your tests if done multiple times, and you will be forced to avoid tying the provider to services from your production code accidentally. You will also have more control over how you create your faker instance and which providers you want to load for specific tests, if that is relevant for you. For instance you could create an instance in the setUp method of your tests:

    class MyTest extends TestCase
    {
        private $faker;
    
        protected function setUp(): void
        {
            // The Faker\Factory will create a ready to use Faker Generator
            $this->faker = Factory::create();
            $this->faker->addProvider(new MyCustomProvider());
        }
    
        public function testSomething(): void
        {
            $title = $this->faker->title;
        }
    }
    

    The Factory will create a new Faker\Generator with all the default providers already registered. You can look at the code if you'd rather only have your own providers in there. Instead of copying this code in each test you can share it, e.g. by creating your own TestCase-class that your tests extend instead of the one provided by PHPUnit or you could use a trait. I think either approach is fine, but probably having your own base TestCase if you are not yet familiar with Traits.

    With the setUp method above you can also look at how Symfony's Service Container works, especially how to create services through a factory and then, for example create a config/services_test.yaml where you create a faker instance that you can get in tests extending the WebTestCase or KernelTestCase after booting your Symfony kernel.