Using factory in PHPUnit provider fails

I found an answer in a different question (caused by the same issue)

So, this issue could be solved by calling $this->createApplication(); or $this->refreshApplication(); in the data provider method according to this answer, or by calling it in the constructor according to this answer

the code will look like this

public function dataProvider() {
    $this->refreshApplication(); // or $this->createApplication();
    $user = factory(\App\Model\User::class)->states('guest')->make();

    // ...
    // another code to generate data ....
    // ...

    return $someFakeData;
}

I tried that and worked, although I feel like its a workaround and not how things should work, any "cleaner" suggestions would be appreciated.


It's possible to use factories inside your data providers and still have database transactions work too!

This was frustrating me today and I figured out a solution inspired by this answer which I found thanks to this answer

It's not pretty but here it is:

Update I also turned this into a blog post which goes a bit more into detail: https://technicallyfletch.com/how-to-use-laravel-factories-inside-a-data-provider/

First I modified the way I consume the provider. Instead of expecting a list of arguments as I normally do, I expect a function from which I can destructure the arguments out of. This is what defers the execution of the factories until after I'm inside my test case.

    /**
     * @test
     * @dataProvider validationProvider
     */
    public function it_validates_payload($getData)
    {
        // data provider now returns a function which we can invoke to
        // destructure our arguments here.
        [$ruleName, $payload] = $getData();

        $this->post(route('participants.store'), $payload)
            ->assertSessionHasErrors($ruleName);
    }

My provider now becomes something like this:

    public function validationProvider()
    {
        return [
            'it fails if participant_type_id does not exist' => [
                function () {
                    return [
                        'participant_type_id',
                        array_merge($this->getValidData(), ['participant_type_id' => null])
                    ];
                }
            ],
            'it fails if first_name does not exist' => [
                function () {
                    return [
                        'first_name',
                        array_merge($this->getValidData(), ['first_name' => null])
                    ];
                }
            ],
            'it fails if last_name does not exist' => [
                function () {
                    return [
                        'last_name',
                        array_merge($this->getValidData(), ['last_name' => null])
                    ];
                }
            ],
            'it fails if email is not unique' => [
                function () {
                    $existingEmail = factory(Participant::class)->create([
                        'email' => '[email protected]'
                    ])->email;
                    return [
                        'email',
                        array_merge($this->getValidData(), ['email' => $existingEmail])
                    ];
                }
            ],
        ];
    }

And then this is sort of beside the point but it illustrates well that you can defer the factories. The getValidData() method is just returning an array of valid data so each test is only asserting one validation error at a time. It too, uses a factory:

    private function getValidData()
    {
        return [
            'practice_id' => factory(Practice::class)->create()->id,
            'participant_type_id' => 1,
            'first_name' => 'John',
            'last_name' => 'Doe',
        ];
    }

Some things that still bug me about this

  1. It's just sloppy looking. Data providers are already difficult to make readable and this just makes it a lot worse. Although you could abstract a utility to help clean this up.
  2. In my example, I have a database call that gets made for every single scenario, since it gets run with each execution of the provider's returned closures... yuk! I'm not sure what the best approach would be to fix that but one way would be to set up some state in the constructor of the class itself. Once the id is created after the first execution of the provider, you could pull the id from state rather than making a new call to the database each time.

Otherwise, it is a working solution for when you need this, and I hope it helps others if they happen to find this!