Testing complex multi-step processes has always been a pain point in software development. Think about a typical user registration flow, an e-commerce checkout, or an IoT device responding to commands. Each process involves multiple services - APIs, database transactions, async protocols, slow network connections. Each step depends on the previous one. Any of them could fail.
I’ve spent years dealing with this challenge, writing integration tests that inevitably become fragile, hard to understand, and leave little room for fuzzing. The worst part? They often don’t even pass in local environments.
So I built something better.
What is lab34-flows?
lab34-flows is an orchestration tool that executes multi-step workflows defined in YAML. Instead of writing code to glue services together, you describe the sequence of operations in a human-readable format.
Here’s what a test case looks like:
title: Verify product stock
description: Creates a product and validates initial stock is zero
steps:
- application: product_catalog
method: api_create_product
parameters:
body:
name: R/C Car
- application: product_catalog
method: database_search_ean
test:
body:
stock_amount: 0
retry:
delay: 5000
times: 10
No code. Just a clear description of what applications are involved and what actions to perform. The tool reads this file and executes the steps in order, handling state and data flow between them.
The Architecture
The system has three main components:
The Tool (lab34-flows): The CLI engine that reads and executes YAML workflow files. It parses steps, calls the appropriate application methods, manages data flow, and runs defined tests.
The Organization Context: A shared folder or repository where developers define integrations. This acts as a bridge between simple YAML files and your actual services. For example, the stripe_gateway application and its tokenize_card method are defined here. This means QA, DevOps, or product managers can write and review test scenarios in plain YAML without knowing the underlying implementation details.
Credentials (.env files): Standard .env files for environment-specific configs like URLs, database credentials, and API keys. By swapping the .env file, you can run the exact same workflow against a local mock, staging, or even production.
Built-in Testing Features
Beyond simple execution, lab34-flows is a powerful testing tool. You can define assertions directly within a step using the test key:
- Status code validation: Ensure an API call returns the expected HTTP status
- Header assertions: Verify certain data is present in HTTP response headers
- Response body assertions: Check response content with simple value matching or complex expressions
- Retry logic: Automatically retry a step a configurable number of times - essential for testing async processes
A Real-World Example: Payment Processing
Here’s how you’d test an entire e-commerce payment flow:
title: Process New Customer Payment
description: Tests the entire payment process from user registration to transaction reporting.
steps:
- application: user_service
method: signup
description: Create user record if not exists
parameters:
body:
userEmail: "customer@example.com"
name: "Jane Doe"
test:
status: [201]
- application: stripe_gateway
method: tokenize_card
description: Securely obtain a token for card details
parameters:
body:
cardNumber: "4242..."
- application: payment_db
method: create_record
description: Create internal DB records for the transaction
parameters:
body:
amount: 1000
currency: "USD"
- application: reporting_service
method: trigger_report_generation
description: Kick off an async report generation job
- application: reporting_service
method: get_report_by_transaction
description: Verify the report has been generated successfully
test:
body:
status: "COMPLETED"
retry:
times: 10
delay: 5000
If user_service returns anything other than 201, the flow stops immediately. It also gracefully handles the reporting service delay by polling until the job completes.
Local Development Without External Dependencies
One of the biggest challenges with complex systems is dealing with external dependencies. How do you run an E2E test of your payment flow without making real API calls to Stripe?
Because each application is a modular component defined in the Context, you can configure the flow runner to substitute a real service with a local mock. In a local or CI environment, you can set up lab34-flows so the stripe_gateway application points to a mock server you control.
This means you can test scenarios where credit card “4242-4242-4242-4242” is expected to work, but “1111-1111-1111-1111” is expected to fail. All from your localhost.
Key Features
- Declarative YAML Workflows: Define complex tests in a simple, human-readable format
- Shared Application Context: A central, versioned repository for defining service integrations
- Environment-Based Credentials: Securely manage API keys and URLs for different environments
- Built-in Assertions and Retries: Validate results directly in YAML with status, body checks, and retries
- Automatic Error Handling: Fail-fast stops the flow on any exception
- Enhanced Testability: Easily mock any application for robust local and CI testing
- Composable Steps: Build flows from a library of predefined, robust steps
Get Started
The project is open source and available on GitHub:
- Repository: github.com/lab34-es/lab34-flows
I built this to solve my own testing and orchestration challenges, and I believe it can help others build more reliable and maintainable applications. Dive into the docs, explore the examples, and let me know what you think. Contributions, feedback, and ideas are welcome!
This post is based on the original article published on Lab34’s blog.