Test Commands
Commands for running flow scenarios and binding tests.
eventflow test
Run flow scenarios as tests.
bash
# Test a single flow file
eventflow test order.flow
# Test flow with test variations
eventflow test order.flow order.test.flow
# Test specific scenario
eventflow test order.flow --scenario="successful checkout"
# Test all flows in directory
eventflow test ./flows/
# Include binding unit tests
eventflow test order.flow --with-bindings
# Watch mode
eventflow test order.flow --watch
# Verbose output
eventflow test order.flow --verbose
# Output formats
eventflow test order.flow --format=pretty # Default
eventflow test order.flow --format=json
eventflow test order.flow --format=junitOptions:
| Option | Description |
|---|---|
--scenario=NAME | Run only the named scenario |
--with-bindings | Also run linked binding unit tests |
--watch | Re-run tests on file changes |
--verbose | Show detailed output |
--format=FORMAT | Output format: pretty, json, junit |
--filter=PATTERN | Filter scenarios by pattern |
Output: Success (All Pass)
$ eventflow test order.flow order.test.flow
EventFlow v1.0.0
Loading flows...
→ order.flow (1 machine, 3 scenarios)
→ order.test.flow (6 test variations)
@order / checkout
────────────────────────────────────────
happy path
✓ checkout → awaiting_payment (18ms)
✓ payment_success → confirmed (21ms)
:checkout variations
✓ empty cart rejected (12ms)
✓ guest user redirected to login (10ms)
:payment_success variations
✓ confirmation email is sent (14ms)
:payment_failed variations
✓ payment failure notifies customer (11ms)
:retry_checkout variations
✓ retry after payment failure (16ms)
✓ retry limit exceeded (13ms)
✓ admin can override retry limit (12ms)
────────────────────────────────────────
✓ 9 passing (127ms)Output: Failure (Some Fail)
$ eventflow test order.flow order.test.flow
EventFlow v1.0.0
Loading flows...
→ order.flow (1 machine, 3 scenarios)
→ order.test.flow (6 test variations)
@order / checkout
────────────────────────────────────────
happy path
✓ checkout → awaiting_payment (18ms)
✗ payment_success → confirmed (21ms)
Expected: order is in #confirmed
Actual: order is in #awaiting_payment
Flow trace:
1. on :checkout from @customer
→ order moves to #awaiting_payment ✓
2. on :payment_success from @payment
→ order moves to #confirmed ✗
Guard 'payment is valid' returned false
at order.flow:45
:checkout variations
✓ empty cart rejected (12ms)
✗ guest user redirected to login (10ms)
Expected: @customer received :login_redirect
Actual: No events emitted to @customer
at order.test.flow:23
────────────────────────────────────────
✓ 2 passing
✗ 2 failing
○ 5 skipped (dependency failed)
Total: 9 scenarios (127ms)Output: Missing Binding
$ eventflow test order.flow
EventFlow v1.0.0
Loading flows...
→ order.flow (1 machine, 3 scenarios)
@order / checkout
────────────────────────────────────────
happy path
✗ checkout → awaiting_payment
Error: No binding found for guard 'cart is not empty'
The flow references a guard that has no PHP implementation:
? cart is not empty ← Missing binding
order moves to #awaiting_payment
Hint: Create a guard class with:
#[Guard('cart is not empty')]
class CartNotEmptyGuard extends GuardBehavior
{
public function __invoke(array $context): bool
{
// TODO: Implement
}
}
Or run: eventflow make:guard "cart is not empty"
at order.flow:12
────────────────────────────────────────
✗ 1 failing (missing binding)
○ 8 skipped
Total: 9 scenarios (23ms)Output: With Bindings (--with-bindings)
$ eventflow test order.flow --with-bindings
EventFlow v1.0.0
Loading flows...
→ order.flow (1 machine, 3 scenarios)
Loading bindings...
→ 6 guards, 4 actions, 3 events (13 total)
→ 8 test classes linked
════════════════════════════════════════
FLOW TESTS
════════════════════════════════════════
@order / checkout
────────────────────────────────────────
happy path
✓ checkout → awaiting_payment (18ms)
✓ payment_success → confirmed (21ms)
:checkout variations
✓ empty cart rejected (12ms)
✓ guest user redirected to login (10ms)
────────────────────────────────────────
✓ 4 passing (61ms)
════════════════════════════════════════
BINDING TESTS
════════════════════════════════════════
Guards
────────────────────────────────────────
CartNotEmptyGuard ('cart is not empty')
✓ returns false when cart is null (2ms)
✓ returns false when cart is empty (1ms)
✓ returns true when cart has items (1ms)
CustomerLoggedInGuard ('@customer is logged in')
✓ returns false when customer is null (2ms)
✓ returns false when not authenticated (1ms)
✓ returns true when authenticated (1ms)
Actions
────────────────────────────────────────
IncrementRetryCountAction ('$retry_count increases by 1')
✓ increments retry count (1ms)
✓ defaults to one when not set (1ms)
────────────────────────────────────────
✓ 8 passing (11ms)
════════════════════════════════════════
SUMMARY
════════════════════════════════════════
Flow scenarios: 4 passing
Binding tests: 8 passing
────────────────────────────
Total: 12 passing (72ms)Output: Watch Mode
$ eventflow test order.flow --watch
EventFlow v1.0.0 (watch mode)
Watching for changes...
→ order.flow
→ order.test.flow
→ App\Order\Guards\*
→ App\Order\Actions\*
Press Ctrl+C to stop
────────────────────────────────────────
[14:32:15] Running tests...
@order / checkout
✓ 9 passing (127ms)
[14:32:45] File changed: CartNotEmptyGuard.php
[14:32:45] Running tests...
@order / checkout
✓ 9 passing (125ms)
[14:33:12] File changed: order.flow
[14:33:12] Running tests...
@order / checkout
✓ 9 passing (128ms)Output: JSON Format
json
{
"version": "1.0.0",
"timestamp": "2024-01-15T14:32:15Z",
"duration_ms": 127,
"flows": ["order.flow", "order.test.flow"],
"summary": {
"total": 9,
"passing": 9,
"failing": 0,
"skipped": 0
},
"machines": [
{
"name": "@order",
"scenarios": [
{
"name": "checkout",
"group": "happy path",
"status": "passed",
"duration_ms": 18,
"assertions": [
{
"expression": "order is in #awaiting_payment",
"passed": true
}
]
}
]
}
]
}Output: JUnit Format
xml
<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="EventFlow" tests="9" failures="0" time="0.127">
<testsuite name="@order / checkout" tests="9" failures="0" time="0.127">
<testcase name="checkout → awaiting_payment" classname="order.checkout.happy_path" time="0.018"/>
<testcase name="payment_success → confirmed" classname="order.checkout.happy_path" time="0.021"/>
<testcase name="empty cart rejected" classname="order.checkout.checkout_variations" time="0.012"/>
</testsuite>
</testsuites>eventflow test:binding
Run unit tests for a specific binding pattern.
bash
# Test by pattern
eventflow test:binding "cart is not empty"
eventflow test:binding "@customer is logged in"
eventflow test:binding "send confirmation email"
# Test by class name
eventflow test:binding CartNotEmptyGuard
# Verbose output
eventflow test:binding "cart is not empty" --verboseOutput: Success
$ eventflow test:binding "cart is not empty"
EventFlow v1.0.0
Testing binding: 'cart is not empty'
────────────────────────────────────────
Binding: App\Order\Guards\CartNotEmptyGuard
Test: Tests\Order\Guards\CartNotEmptyGuardTest
✓ test_returns_false_when_cart_is_null (2ms)
✓ test_returns_false_when_cart_is_empty (1ms)
✓ test_returns_true_when_cart_has_items (1ms)
────────────────────────────────────────
✓ 3 passing (4ms)Output: Failure
$ eventflow test:binding "cart is not empty"
EventFlow v1.0.0
Testing binding: 'cart is not empty'
────────────────────────────────────────
Binding: App\Order\Guards\CartNotEmptyGuard
Test: Tests\Order\Guards\CartNotEmptyGuardTest
✓ test_returns_false_when_cart_is_null (2ms)
✗ test_returns_false_when_cart_is_empty (3ms)
Expected: false
Actual: true
$cart = new \stdClass();
$cart->items = [];
$context = ['cart' => $cart];
$this->assertFalse(($this->guard)($context));
↑
Returns true instead of false
at tests/Order/Guards/CartNotEmptyGuardTest.php:34
✓ test_returns_true_when_cart_has_items (1ms)
────────────────────────────────────────
✓ 2 passing
✗ 1 failingOutput: Binding Not Found
$ eventflow test:binding "cart is not empty"
EventFlow v1.0.0
Testing binding: 'cart is not empty'
────────────────────────────────────────
✗ Error: No binding found for pattern 'cart is not empty'
Searched in:
→ App\Order\Guards
→ App\Order\Actions
→ App\Checkout\Guards
Hint: Create a guard class with:
#[Guard('cart is not empty')]
class CartNotEmptyGuard extends GuardBehavior
{
public function __invoke(array $context): bool
{
// TODO: Implement
}
}
Or run: eventflow make:guard "cart is not empty"Output: Test Not Found
$ eventflow test:binding "cart is not empty"
EventFlow v1.0.0
Testing binding: 'cart is not empty'
────────────────────────────────────────
Binding: App\Order\Guards\CartNotEmptyGuard
Test: Not linked
⚠ Warning: No test class linked to this binding
The binding exists but has no #[TestedBy] attribute
and no test class with #[Tests('cart is not empty')]
Hint: Create a test class with:
#[Tests('cart is not empty')]
class CartNotEmptyGuardTest extends TestCase
{
public function test_example(): void
{
$guard = new CartNotEmptyGuard();
// Add assertions
}
}
Or run: eventflow make:binding-test "cart is not empty"Output: Verbose Mode
$ eventflow test:binding "cart is not empty" --verbose
EventFlow v1.0.0
Testing binding: 'cart is not empty'
────────────────────────────────────────
Binding Details:
Pattern: 'cart is not empty'
Type: Guard
Class: App\Order\Guards\CartNotEmptyGuard
File: src/Order/Guards/CartNotEmptyGuard.php
TestedBy: CartNotEmptyGuardTest::class
Test Details:
Class: Tests\Order\Guards\CartNotEmptyGuardTest
File: tests/Order/Guards/CartNotEmptyGuardTest.php
Tests: #[Tests('cart is not empty')]
Running tests...
✓ test_returns_false_when_cart_is_null (2ms)
Input: ['cart' => null]
Output: false
✓ test_returns_false_when_cart_is_empty (1ms)
Input: ['cart' => {items: []}]
Output: false
✓ test_returns_true_when_cart_has_items (1ms)
Input: ['cart' => {items: [{id: 1}]}]
Output: true
────────────────────────────────────────
✓ 3 passing (4ms)
Coverage:
Lines: 12/12 (100%)
Branches: 4/4 (100%)eventflow test:bindings
Run unit tests for all bindings.
bash
# All bindings
eventflow test:bindings
# Bindings for a specific flow
eventflow test:bindings order.flow
# Filter by type
eventflow test:bindings --type=guard
eventflow test:bindings --type=action
eventflow test:bindings --type=event
# Specific namespace
eventflow test:bindings --namespace="App\\Order"Options:
| Option | Description |
|---|---|
--type=TYPE | Filter by binding type: guard, action, event |
--namespace=NS | Filter by namespace |
--coverage | Show coverage report |
Output: All Bindings
$ eventflow test:bindings
EventFlow v1.0.0
Loading bindings...
→ 6 guards, 4 actions, 3 events (13 total)
→ 11 test classes linked
════════════════════════════════════════
GUARDS
════════════════════════════════════════
CartNotEmptyGuard ('cart is not empty')
✓ test_returns_false_when_cart_is_null (2ms)
✓ test_returns_false_when_cart_is_empty (1ms)
✓ test_returns_true_when_cart_has_items (1ms)
CustomerLoggedInGuard ('@customer is logged in')
✓ test_returns_false_when_customer_is_null (2ms)
✓ test_returns_false_when_not_authenticated (1ms)
✓ test_returns_true_when_authenticated (1ms)
RetryLimitGuard ('$retry_count is less than 3')
✓ test_returns_true_when_retry_count_is_zero (1ms)
✓ test_returns_true_when_below_limit (1ms)
✓ test_returns_false_when_at_limit (1ms)
✓ test_returns_false_when_above_limit (1ms)
✓ test_defaults_to_zero (1ms)
CustomerIsAdminGuard ('@customer is admin')
✓ test_returns_false_when_customer_is_null (1ms)
✓ test_returns_false_when_not_admin (1ms)
✓ test_returns_true_when_admin (1ms)
PaymentAuthorizedGuard ('payment is authorized')
⚠ No tests linked
OrderInStateGuard ('order is in {state}')
✓ test_returns_true_when_in_state (1ms)
✓ test_returns_false_when_not_in_state (1ms)
════════════════════════════════════════
ACTIONS
════════════════════════════════════════
IncrementRetryCountAction ('$retry_count increases by 1')
✓ test_increments_retry_count (1ms)
✓ test_defaults_to_one_when_not_set (1ms)
SendConfirmationEmailAction ('send confirmation email')
✓ test_sends_confirmation_email (3ms)
✓ test_handles_missing_customer_gracefully (1ms)
LogPaymentAttemptAction ('log payment attempt')
✓ test_logs_attempt (2ms)
NotifyCustomerAction ('notify customer')
⚠ No tests linked
════════════════════════════════════════
EVENTS
════════════════════════════════════════
CheckoutEvent (':checkout')
✓ test_validates_payload (1ms)
PaymentRequestEvent (':payment_request')
✓ test_includes_order_id (1ms)
✓ test_includes_amount (1ms)
OrderConfirmedEvent (':order_confirmed')
⚠ No tests linked
════════════════════════════════════════
SUMMARY
════════════════════════════════════════
Guards: 14/16 tests passing (4 bindings, 1 untested)
Actions: 5/5 tests passing (3 bindings, 1 untested)
Events: 3/3 tests passing (2 bindings, 1 untested)
─────────────────────────────────────────────────────
Total: 22/24 tests passing (72ms)
⚠ 3 bindings have no test coverage:
→ PaymentAuthorizedGuard
→ NotifyCustomerAction
→ OrderConfirmedEventOutput: For Flow File
$ eventflow test:bindings order.flow
EventFlow v1.0.0
Analyzing order.flow...
→ 4 guards required
→ 2 actions required
→ 3 events required
Testing bindings used in order.flow...
════════════════════════════════════════
GUARDS (used in order.flow)
════════════════════════════════════════
CartNotEmptyGuard ('cart is not empty')
✓ 3 tests passing
CustomerLoggedInGuard ('@customer is logged in')
✓ 3 tests passing
RetryLimitGuard ('$retry_count is less than 3')
✓ 5 tests passing
CustomerIsAdminGuard ('@customer is admin')
✓ 3 tests passing
════════════════════════════════════════
ACTIONS (used in order.flow)
════════════════════════════════════════
IncrementRetryCountAction ('$retry_count increases by 1')
✓ 2 tests passing
SendConfirmationEmailAction ('send confirmation email')
✓ 2 tests passing
════════════════════════════════════════
SUMMARY
════════════════════════════════════════
Bindings for order.flow: 6/6 implemented
Tests: 18/18 passing (32ms)