0

I have a number of factories generating models using faker, as recommended in the documentation:

$factory->define(App\Member::class, function (Faker $faker) {
    return [
        'name' => $faker->name()
    ];
});

I would like to use these factories in tests in order to seed the database into a known state. Faker (by its nature) is creating random data when I call factory(\App\Member::class, 10) which makes an assertion like $this->assertEquals('Eve', Member::find(5)->name) fail on future runs.

I note that Faker has a seed method to allow deterministic data generation:

$faker = Faker\Factory::create();
$faker->seed(1234);

However, with the factory(\App\Member::class, 10) interface there appears to be no way to set the seed of the Faker instance used in the factory.

Is there a way to set the Faker seed from the test case?

Or failing this what at the best practices for setting the database state in a test (I would like to use factories but maybe this is not the best approach)?

thodic
  • 2,229
  • 1
  • 19
  • 35

1 Answers1

0

Faker is used with your factories in order to allow you to quickly create model instances without having to provide all the data yourself within your tests. It's much easier to simply do:

factory(User::class)->create();

...than it is to manually specify all of the fields that the User model requires. Faker provides random, sample data for all of the fields as specified in your factory definition.

When asserting in your tests, you shouldn't rely on knowing what that random data will be ahead of time. You can provide attributes yourself, which are merged into the random data defined in your factory and you can use this data to make your assertions.

A simple, trivial example:

$user = factory(User::class)->create(['name' => 'Joe Bloggs']);
$this->assertEquals('Joe Bloggs', $user->name);

The benefit of this is that you only need to provide the attributes you're interested in for your test and you can let your factory take care of providing the rest of the data.

You could of course allow your factory to provide the attributes and then use that information in the generated model(s) to check the data in your database. Something like this:

$user = factory(User::class)->create(['enabled' => false]);
$user->enableUser();
$this->seeInDatabase((new User)->getTable(), [
    'id' => $user->id,
    'name' => $user->name,
    'enabled' => true
]);

In this example, the factory is used to create a user, providing the enabled attribute to be false. The method you're testing, in this case enableUser is run. Then you can use the ID and the name of the user generated by the factory in your where part of seeInDatabase, along with 'enabled' => true to ensure that the database was updated for the generated user, by setting the enabled field to true but leaving the generated name unchanged.

Jonathon
  • 15,873
  • 11
  • 73
  • 92