Testing
Bag supports Factories to make creating test values easier. Bag factories are similar to Eloquent factories, but they are used to create Bag objects.
Creating a Factory
Factories extend the Bag\Factory class, and define a definition() method that returns an array of default values for the value object.
use Bag\Factory;
class MyValueFactory extends Factory {
#[Override]
public function definition(): array {
return [
'name' => 'Davey Shafik',
'age' => 40,
];
}
}Faker Integration
Factories include Faker support out of the box. You can use the $faker property to generate random values:
return [
'name' => $this->faker->name(),
'age' => $this->faker->numberBetween(18, 65),
];TIP
You can also generate factory classes automatically using the [artisan make:bag][./laravel-artisan-make-bag-command] command.
Using a Factory
Before you can use a Factory, you must first add both the Factory attribute and the HasFactory trait to your Bag object:
use Bag\Attributes\Factory;
use Bag\Bag;
use Bag\Traits\HasFactory;
#[Factory(MyValueFactory::class)]
class MyValue extends Bag {
use HasFactory;
public function __construct(
public string $name,
public int $age,
) {}
}You can now use the factory to create a new instance of the value object:
$bag = MyValue::factory()->make();This will create a new MyValue object using the factory definition.
Customizing Factory State
You can also specify custom values when creating a factory, which will override the factory definition. You can pass the values to the ::factory() call itself, using the ->state() method on the factory, or by passing it to the ->make() method.
// All three are identical:
$value = MyValue::factory([
'name' => 'Taylor Otwell',
])->make();
$value = MyValue::factory()->make([
'name' => 'Taylor Otwell',
]);
$value = MyValue::factory()->state([
'name' => 'Taylor Otwell',
])->make();Named States
Bag supports named states, which allow you to modify the state of the value object when creating it:
use Bag\Factory;
class MyValueFactory extends Factory {
public function definition(): array {
return [
'name' => 'Davey Shafik',
'age' => 40,
];
}
public function withName(string $name): static {
return $this->state([
'name' => $name,
]);
}
}You can now use the state when creating the value object:
$bag = MyValue::factory()->withName($faker->name())->make();Creating Collections of Bag Values
You can use the ->count() method to create a collection of Bag objects:
$values = MyValue::factory()->count(10)->make();This will create a Bag\Collection of 10 identical MyValue objects.
TIP
If your Bag object has a Collection attribute, ->make() will return an instance of that collection class.
Sequences
Bag factories support Eloquent factory Sequences to generate unique values for each instance in a collection.
use Illuminate\Database\Eloquent\Factories\Sequence;
$bag = MyValue::factory()->count(10)->sequence(fn(Sequence $sequence) => [
'name' => 'Person #' . $sequence->index,
'age' => 18 + $sequence->index,
])->make();In this example, the name property will be set to Person #1, Person #2, etc., and the age property will be set to 19, 20, etc.
The ->sequence() method accepts any of the following:
- A
Illuminate\Database\Eloquent\Factories\Sequenceinstance created with aclosurethat returns an array of values - A
Illuminate\Database\Eloquent\Factories\Sequenceinstance created with a variadic number of arrays of values - A
closurevalue that returns an array of values - A variadic number of arrays of values
You may also pass a Sequence object to the ->state() method.
TIP
If you create more values than number of value arrays passed in, the sequence will start over from the beginning.
WARNING
If you use both states (named or via the ::factory(), ->state(), or ->make() methods) and sequences, sequences will be applied after the state, so the sequences will override any values set by the state.