Testing in Phlow
Phlow provides a built-in testing framework that allows you to write and run tests directly in your .phlow
files. This documentation covers how to write, run, and understand tests in Phlow.
For practical examples with real test outputs, see the Basic Testing Examples section.
Running Testsโ
To run tests in a Phlow file, use the --test
or -t
flag:
phlow --test main.phlow
When you run tests, Phlow will:
- Load the Phlow file
- Download any required modules
- Execute each test case
- Display the results with a summary
Filtering Testsโ
You can filter tests by description using the --test-filter
flag. This will only run tests whose describe
field contains the specified substring:
# Run only tests with "addition" in their description
phlow --test --test-filter "addition" main.phlow
# Run only tests with "user" in their description
phlow --test --test-filter "user" main.phlow
Example with test descriptions:
name: Calculator Test Suite
version: 1.0.0
description: Comprehensive calculator tests
tests:
- describe: "addition with positive numbers"
main: { operation: "add", a: 5, b: 3 }
payload: null
assert: !phs payload == 8
- describe: "addition with negative numbers"
main: { operation: "add", a: -5, b: 3 }
payload: null
assert: !phs payload == -2
- describe: "subtraction basic test"
main: { operation: "subtract", a: 10, b: 4 }
payload: null
assert: !phs payload == 6
steps:
- assert: !phs main.operation == "add"
then:
payload: !phs main.a + main.b
- assert: !phs main.operation == "subtract"
then:
payload: !phs main.a - main.b
Running with filter:
# This will only run the two addition tests
phlow --test --test-filter "addition" calculator.phlow
Output:
๐งช Running 2 test(s) matching 'addition' (out of 3 total)...
Test 1: addition with positive numbers - โ
PASSED - Assertion passed: {{ payload == 8 }}
Test 2: addition with negative numbers - โ
PASSED - Assertion passed: {{ payload == -2 }}
๐ Test Results:
Total: 2
Passed: 2 โ
Failed: 0 โ
๐ All tests passed!
Test Structureโ
Tests are defined in the tests
section of your Phlow file. Each test case consists of:
main
: Input values for the main contextpayload
: Initial payload value (optional)assert
: Expression-based assertion using PHSassert_eq
: Direct value comparison assertion
Basic Test Exampleโ
name: Basic Math Test
version: 1.0.0
description: Testing basic arithmetic operations
tests:
- main:
x: 10
y: 20
payload: 5
assert: !phs payload == 35
- main:
x: 0
y: 0
payload: 100
assert: !phs payload == 100
- main:
x: -5
y: 5
payload: 10
assert: !phs payload > 0
steps:
- payload: !phs main.x + main.y + payload
Test Outputโ
When you run the above test, you'll see output like this:
[2025-07-15T00:07:55Z INFO phlow::loader] Downloading modules...
[2025-07-15T00:07:55Z INFO phlow::loader] All modules downloaded and extracted successfully
๐งช Running 3 test(s)...
Test 1: โ
PASSED - Assertion passed: {{ payload == 35 }}
Test 2: โ
PASSED - Assertion passed: {{ payload == 100 }}
Test 3: โ
PASSED - Assertion passed: {{ payload > 0 }}
๐ Test Results:
Total: 3
Passed: 3 โ
Failed: 0 โ
๐ All tests passed!
Assertion Typesโ
Expression Assertions (assert
)โ
Expression assertions use PHS (Phlow Scripting) to evaluate conditions:
tests:
- main:
name: "John"
age: 25
payload: "active"
assert: !phs payload == "active"
- main:
count: 10
payload: 5
assert: !phs payload < main.count
- main:
items: [1, 2, 3]
payload: 3
assert: !phs payload == main.items.length
Direct Value Assertions (assert_eq
)โ
Direct value assertions compare the final payload with an expected value:
tests:
- main:
multiplier: 2
payload: 10
assert_eq: "Total is 20"
- main:
name: "Alice"
payload: "Hello"
assert_eq: "Hello Alice"
steps:
- payload: !phs main.multiplier * payload
- payload: !phs `Total is ${payload}`
Testing with Modulesโ
You can test workflows that use modules:
name: HTTP Request Test
version: 1.0.0
description: Testing HTTP requests
modules:
- module: http_request
version: latest
tests:
- main:
url: "https://httpbin.org/json"
payload: null
assert: !phs payload.slideshow != null
steps:
- http_request:
url: !phs main.url
method: GET
Complex Test Scenariosโ
Testing Conditional Logicโ
name: Age Verification Test
version: 1.0.0
description: Testing age verification logic
tests:
- main:
age: 25
payload: null
assert: !phs payload == "Adult"
- main:
age: 16
payload: null
assert: !phs payload == "Minor"
- main:
age: 18
payload: null
assert: !phs payload == "Adult"
steps:
- assert: !phs main.age >= 18
then:
payload: "Adult"
else:
payload: "Minor"
Testing Data Transformationโ
name: Data Processing Test
version: 1.0.0
description: Testing data transformation
tests:
- main:
users: [
{"name": "John", "age": 30},
{"name": "Jane", "age": 25}
]
payload: null
assert: !phs payload.length == 2
- main:
users: [
{"name": "Bob", "age": 35}
]
payload: null
assert: !phs payload[0].status == "processed"
steps:
- payload: !phs main.users.map(user => ({ ...user, status: "processed" }))
Test Failuresโ
When tests fail, you'll see detailed error messages:
๐งช Running 2 test(s)...
Test 1: โ FAILED - Expected Total is 20, got Total is 30
Test 2: โ
PASSED - Assertion passed: {{ payload == "Total is 15" }}
๐ Test Results:
Total: 2
Passed: 1 โ
Failed: 1 โ
โ Some tests failed!
Test Best Practicesโ
1. Use Descriptive Test Namesโ
name: User Registration Validation
description: Tests for user registration validation rules
tests:
- main:
email: "user@example.com"
password: "secure123"
payload: null
assert: !phs payload.valid == true
2. Test Edge Casesโ
tests:
- main:
value: 0
payload: null
assert: !phs payload == "zero"
- main:
value: -1
payload: null
assert: !phs payload == "negative"
- main:
value: null
payload: null
assert: !phs payload == "null"
3. Test Error Conditionsโ
tests:
- main:
input: ""
payload: null
assert: !phs payload.error == "Input cannot be empty"
- main:
input: "invalid"
payload: null
assert: !phs payload.error != null
Advanced Testing Featuresโ
Testing Asynchronous Operationsโ
name: Async Operation Test
version: 1.0.0
description: Testing asynchronous operations
modules:
- module: http_request
version: latest
- module: sleep
version: latest
tests:
- main:
delay: 1
payload: "start"
assert: !phs payload == "completed"
steps:
- sleep:
seconds: !phs main.delay
- payload: "completed"
Testing Multiple Scenariosโ
name: Calculator Test Suite
version: 1.0.0
description: Comprehensive calculator tests
tests:
# Addition tests
- main: { operation: "add", a: 5, b: 3 }
payload: null
assert: !phs payload == 8
- main: { operation: "add", a: -5, b: 3 }
payload: null
assert: !phs payload == -2
# Subtraction tests
- main: { operation: "subtract", a: 10, b: 4 }
payload: null
assert: !phs payload == 6
- main: { operation: "subtract", a: 0, b: 5 }
payload: null
assert: !phs payload == -5
# Multiplication tests
- main: { operation: "multiply", a: 6, b: 7 }
payload: null
assert: !phs payload == 42
- main: { operation: "multiply", a: -3, b: 4 }
payload: null
assert: !phs payload == -12
steps:
- assert: !phs main.operation == "add"
then:
payload: !phs main.a + main.b
- assert: !phs main.operation == "subtract"
then:
payload: !phs main.a - main.b
- assert: !phs main.operation == "multiply"
then:
payload: !phs main.a * main.b
Debugging Failed Testsโ
When tests fail, you can debug them by:
-
Running the flow normally to see the actual output:
phlow main.phlow
-
Adding debug information to your steps:
steps:
- log: !phs `Debug: main = ${JSON.stringify(main)}`
- log: !phs `Debug: payload = ${JSON.stringify(payload)}`
- payload: !phs main.x + main.y + payload -
Using the
--show-steps
flag to see step execution:phlow --show-steps --test main.phlow
Testing CLI Applicationsโ
For CLI applications, you can test argument processing:
name: CLI Application Test
version: 1.0.0
description: Testing CLI argument processing
main: cli
modules:
- module: cli
with:
args:
- name: name
description: User name
index: 1
type: string
required: true
- name: age
description: User age
index: 2
type: number
required: true
tests:
- main:
name: "John"
age: 25
payload: null
assert: !phs payload == "John (25 years old)"
- main:
name: "Jane"
age: 30
payload: null
assert: !phs payload == "Jane (30 years old)"
steps:
- payload: !phs `${main.name} (${main.age} years old)`
Integration with CI/CDโ
You can integrate Phlow tests into your CI/CD pipeline:
#!/bin/bash
# test-runner.sh
# Run all tests in the project
for test_file in tests/*.phlow; do
echo "Running tests in $test_file"
if ! phlow --test "$test_file"; then
echo "Tests failed in $test_file"
exit 1
fi
done
echo "All tests passed!"
Common Testing Patternsโ
Setup and Teardownโ
name: Database Test
version: 1.0.0
description: Testing database operations
modules:
- module: postgres
version: latest
tests:
- main:
table: "users"
data: {"name": "John", "email": "john@example.com"}
payload: null
assert: !phs payload.success == true
steps:
# Setup
- postgres:
query: "CREATE TABLE IF NOT EXISTS users (id SERIAL PRIMARY KEY, name VARCHAR(100), email VARCHAR(100))"
# Test operation
- postgres:
query: !phs `INSERT INTO users (name, email) VALUES ('${main.data.name}', '${main.data.email}')`
# Verify result
- postgres:
query: !phs `SELECT * FROM users WHERE email = '${main.data.email}'`
- payload: !phs { success: payload.length > 0 }
# Teardown
- postgres:
query: "DROP TABLE IF EXISTS users"
This comprehensive testing framework makes it easy to ensure your Phlow applications work correctly and reliably across different scenarios and inputs.