Verbs

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() and create(), 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 StateFactory
2{
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(): ExampleStateFactory
6{
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(): void
2{
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(): array
2{
3 return [
4 'example_count' => 4,
5 ];
6}