Skip to content

Scenarios as Tests

In EventFlow, every scenario with assertions is an executable test. Your documentation IS your test suite.

Scenarios ARE Tests

The test structure follows the familiar Given-When-Then pattern:

  • Given: Initial state (given: block)
  • When: Event trigger (on :event)
  • Then: Assertions (expect: block)
flow
machine: @shopping_cart

scenario: add item to empty cart

  given:
    @customer is logged in
    cart is empty
    $total: number is 0

  on> :add_item from @customer
    $items adds $item
    $total increases by $item.price

    expect:
      = $items contains $item

  expect:
    = $items_count equals 1
    = $total equals 1200

Run with:

bash
eventflow test cart.flow

Assertion Levels

Event-Level Assertions (Optional)

Verify after each event:

flow
on> :checkout from @customer
  order moves to #awaiting_payment

  expect:
    = order is in #awaiting_payment

Scenario-Level Assertions

Verify after all events complete:

flow
scenario: complete purchase

  on> :checkout from @customer
    order moves to #awaiting_payment

  on :payment_success from @payment
    order moves to #paid

  on :stock_reserved from @inventory
    order moves to #fulfilled

  expect:
    = order is in #fulfilled
    = @customer received :order_confirmed

Recommendation

  • Use scenario-level expect: for main test assertions
  • Use event-level expect: for debugging or critical checkpoints

Test Granularity

LevelWhatEventFlowBinding Code
UnitIndividual action/guardN/ATest each binding class
IntegrationEvent handlerEvent-level expectTest event flow
FeatureFull scenarioScenario-level expectTest complete feature

Complete Test Example

flow
machine: @order

scenario: successful checkout

  given:
    @customer is logged in as "[email protected]"
    @customer has verified payment method
    cart contains:
      | product | price |
      | Laptop  | 1200  |
      | Mouse   | 25    |
    $total: number is 1225

  on> :checkout from @customer
    ? cart is not empty
      order moves to #awaiting_payment
      emit :payment_request to @payment

    expect:
      = order is in #awaiting_payment
      = :payment_request was emitted to @payment

  on :payment_success from @payment
    order moves to #paid
    $paid_at becomes now
    emit :order_confirmed to @customer

    expect:
      = order is in #paid
      = $paid_at is not empty

  expect:
    = order is in #paid
    = @customer received :order_confirmed
    = $total equals 1225

Assertion Patterns

State Assertions

flow
expect:
  = order is in #paid
  = order is not in #cancelled
  = order has entered #processing

Context Assertions

flow
expect:
  = $total equals 1200
  = $items_count equals 3
  = $discount is greater than 0
  = $customer_email is not empty

Event Assertions

flow
expect:
  = @customer received :order_confirmed
  = :payment_request was emitted to @payment
  = @warehouse received :prepare_shipment

Collection Assertions

flow
expect:
  = $items contains "Laptop"
  = $items is not empty
  = $tags includes "urgent"

Testing Multiple Scenarios

flow
machine: @order

scenario: successful checkout
  given:
    cart has items
    payment method is valid

  on> :checkout from @customer
    order moves to #paid

  expect:
    = order is in #paid

scenario: checkout with empty cart
  given:
    cart is empty

  on> :checkout from @customer
    emit :empty_cart_error to @customer

  expect:
    = order is not in #paid
    = @customer received :empty_cart_error

scenario: checkout with payment failure
  given:
    cart has items
    payment will fail

  on> :checkout from @customer
    order moves to #awaiting_payment

  on :payment_failed from @payment
    order moves to #payment_failed

  expect:
    = order is in #payment_failed

Test Runner Commands

bash
# Run all scenarios in a file
eventflow test order.flow

# Run specific scenario
eventflow test order.flow --scenario="successful checkout"

# Run all tests in directory
eventflow test ./flows/

# Run with verbose output
eventflow test order.flow --verbose

# Watch mode (re-run on file changes)
eventflow test order.flow --watch

Testing Systems

Test entire multi-machine systems:

bash
# Test system with all machines
eventflow test system.flow

# Test specific flow across machines
eventflow test system.flow --scenario="complete purchase"

Test Output

eventflow test order.flow

 order.flow

 ✓ successful checkout (23ms)
 ✓ checkout with empty cart (12ms)
 ✓ checkout with payment failure (18ms)

 3 passing (53ms)

With failures:

eventflow test order.flow

 order.flow

 ✓ successful checkout (23ms)
 ✗ checkout with empty cart (15ms)
   Expected: order is in #error
   Actual: order is in #awaiting_payment

 1 passing, 1 failing (38ms)

Best Practices

Test Happy Path and Edge Cases

flow
scenario: happy path - successful order
  // Normal flow

scenario: edge case - empty cart
  // Edge case

scenario: error case - payment failure
  // Error handling

Use Descriptive Scenario Names

flow
// Good
scenario: customer can checkout with valid payment
scenario: checkout fails when cart is empty
scenario: order cancelled after 3 payment failures

// Avoid
scenario: test 1
scenario: checkout test

Keep Scenarios Focused

flow
// Good - focused on one thing
scenario: add item updates total
  given:
    $total: number is 100

  on> :add_item from @customer
    $total increases by $item.price

  expect:
    = $total equals 150

// Avoid - testing too many things
scenario: full order flow test
  // 50 lines of setup and assertions

Released under the MIT License.