Testing
We enjoy improving Verbs by providing easy, readable testing affordances.
Verbs::commit()
When testing verbs events, you'll need to call commit manually.
You may continue manually adding Verbs::commit()
after each Event::fire()
method; however, we've created Verbs::commitImmediately
to issue a blanket commit on all events you fire in tests.
1beforeEach(function () {2 Verbs::commitImmediately();3});
You may also implement the CommitsImmediately
interface directly on an Event.
(more about this in VerbsStatesInitialized
)
Assertions
The following Test assert()
methods are available to thoroughly check your committing granularly.
Before using these methods, add Verbs::fake()
to your test so Verbs can set up a fake event store to isolate the testing environment.
1Verbs::assertNothingCommitted();2Verbs::assertCommitted(...);3Verbs::assertNotCommitted(...);
State Factories
In tests, you may find yourself needing to fire and commit several events in order to bring your State to the point where it actually needs testing.
The State::factory()
method allows you to bypass manually building up the State, functioning similarly to Model::factory()
.
This allows you to call:
1BankAccountState::factory()->create( 2 data: ['balance' => 1337] 3 id: $bank_account_id 4); 5 6// Or, using `id()` syntax: 7 8BankAccountState::factory() 9 ->id($bank_account_id)10 ->create(11 data: ['balance' => 1337]12 );
- If you accidentally pass an ID into both
id()
andcreate()
,create()
takes precedence.
Or, in the case of a singleton state:
1ChurnState::factory()->create(['churn' => 40]);
Next, we'll get into how these factories work, and continue after with some Verbs factory methods you may already be familiar with from Eloquent factories.
VerbsStateInitialized
Under the hood, these methods will fire (and immediately commit) a new VerbsStateInitialized
event, which will fire onto the given state, identified by the id argument (if id is null, we assume it is a singleton) and return a copy of that state.
This is primarily designed for booting up states for testing. If you are migrating non-event-sourced codebases to Verbs, when there is a need to initiate a state for legacy data, it's better to create a custom MigratedFromLegacy
event.
You may also change the initial event fired from the StateFactory from VerbsStateInitialized
to an event class of your choosing by setting an $intial_event
property on your State Factory.
1class ExampleStateFactory extends StateFactory2{3 protected $initial_event = ExampleCreated::class;4}
VerbsStateInitialized
implements the CommitsImmediately
interface detailed above, so if you change from this initial event makes sure to extend the interface on your replacement event.
Factory Methods
Some methods accept Verbs IDs, which, written longform, could be any of these types: Bits|UuidInterface|AbstractUid|int|string
.
For brevity, this will be abbreviated in the following applicable methods as Id
.
count(int $count)
Number of states to create. Returns a StateCollection
.
1UserState::factory()->count(3)->create();
id(Id $id)
Set the state ID explicitly (cannot be used with count
).
1UserState::factory()->id(123)->create();
singleton()
Mark that this is a singleton state (cannot be used with count
).
1UserState::factory()->singleton()->create();
state(callable|array $data)
Default data (will be overridden by create
).
1UserState::factory()->state([ /* state data */ ])->create();
The state function is mostly useful for custom factories.
create(array $data, Id|null $id = null)
Explicit state data. Returns a State
or StateCollection
.
1UserState::factory()->create([ /* state data */ ]);
Custom Factories
Verbs makes it possible to create your own custom factories for your states.
Create an ExampleStateFactory
class in a new App/States/Factory
folder.
1namespace App\States\Factories; 2 3use Thunk\Verbs\StateFactory; 4 5class ExampleStateFactory extends StateFactory 6{ 7 public function confirmed(): static 8 { 9 return $this->state(['confirmed' => true]);10 }11}
Now in your ExampleState
, link our new custom factory:
1public bool $confirmed = false;2 3public int $example_count = 0;4 5public static function newFactory(): ExampleStateFactory6{7 return ExampleStateFactory::new(static::class);8}
This lets you do:
1ExampleState::factory()->confirmed()->create(); // ->confirmed will be true
If you'd like to chain behavior after your Factory create()
executes, do so in your configure()
method:
configure()
The configure method in your custom factory allows you to set afterMaking
and afterCreating
effects (see laravel docs).
afterMaking()
& afterCreating()
1public function configure(): void2{3 $this->afterCreating(function (ExampleState $state) {4 ExampleEvent::fire(5 id: $state->id,6 );7 });8}
definition()
Returns an array of default property values for your custom state factory whenever you create()
.
1public function definition(): array2{3 return [4 'example_count' => 4,5 ];6}