Workflow Tests
Overview
A workflow test is a workflow that verifies the behavior of another workflow, service, or Groovy function — the system under test (SUT) — by invoking it with sample input and asserting on its output. Workflow tests are one of the two test types surfaced in Test Explorer, alongside HTTP request integration tests, and are listed there under the Unit Tests in the Test Explorer view.
What You Will Learn
- How Test Explorer identifies and collects workflow tests, and how to exclude one from a run
- Recommended (not enforced) folder structure and naming conventions for organizing your tests
- The required node-graph contract a workflow test must follow to report a pass or fail correctly
- How to create a workflow test using the workflow-test wizard
- How to create a workflow test by asking the Designer agent
Identification and Collection
Test Explorer only treats a workflow as a workflow test if its filename ends with Test.workflow. Any matching
workflow saved inside a Martini package's code directory is picked up automatically — you don't need to register it
manually. In the Navigator, the code directory is labeled Logic. For the full
picture of how Test Explorer's tree is organized and refreshed, see
Testing in Martini Designer.
The Unit Tests tree in Test Explorer updates automatically whenever a matching workflow test is created, deleted, moved, or renamed — there's no need to manually refresh after these changes.
To exclude a test from execution while still keeping it visible in the tree, name it with a SkipTest.workflow
suffix instead of Test.workflow — for example, PlaceOrderSkipTest.workflow. Skipped tests are still listed in
Test Explorer and included in test runs, but they aren't executed and are reported as skipped. For details on how
skipped tests behave within a run, see Running Tests & Test Results.
Recommended Conventions
The naming rules above are the only ones Test Explorer enforces. The following folder and naming conventions aren't required, but following them keeps a growing test suite predictable and easy to navigate.
Folder Structure
Rather than saving a test workflow next to the production workflow it exercises, mirror the SUT's folder structure
under a dedicated code/tests/ subtree. For example:
| System under test | Workflow test |
|---|---|
packages/<package>/code/orders/CreateOrder.workflow |
packages/<package>/code/tests/orders/CreateOrderTest.workflow |
In the Navigator view, a Martini package's code directory is labeled Logic, so in practice you'd create this
structure by adding a tests folder under a package's Logic directory and reproducing the subfolders of the workflow,
service, or function you're testing.
This convention keeps your production workflows uncluttered and makes a test's location predictable directly from its SUT's path — you always know where to look for the test covering a given workflow, service, or function.
Naming
Base the descriptive part of a test's filename on the SUT's own name, following the <SUTName>Test.workflow
pattern — for example, CreateOrder.workflow becomes CreateOrderTest.workflow. This mirrors common unit-test
naming conventions such as JUnit's <ClassName>Test, so a reader familiar with those conventions immediately
recognizes what a test file targets.
When you need multiple test workflows against the same SUT — for instance, a happy path, a validation failure, and
an edge case — insert a scenario descriptor between the SUT name and the Test suffix:
<SUTName>_<Scenario>Test.workflow. For example:
CreateOrder_HappyPathTest.workflowCreateOrder_InvalidInputTest.workflow
The Required Test Contract
When a workflow test runs, Test Explorer reads a boolean output property named success from the test's execution
result. If that property isn't set to exactly true, the test is reported as failed — even if every node in the
test executed without error. This is the one behavioral contract every workflow test's node graph must satisfy, and
it shapes how you build the graph in the Workflow Designer:
- (Optional) Setup — Add nodes before the SUT invocation to prepare test data or fixtures, such as inserting rows a database-driven SUT expects to find.
- Invoke the SUT — Call the workflow, service, or function under test using the node type that matches it: Invoke Workflow for a workflow, Invoke Service for a service, or Invoke Function for a Groovy or Java function. Map the SUT's output properties onto declared properties directly on this invoke node's Data Mapper Panel. A separate Map node placed after the invoke node won't work for this — the SUT's raw output is only accessible on the invoke node itself.
- Assert — Chain one or more Invoke Function nodes on the invoke
node's success path, each calling a function from
io.toro.martini.AssertionMethodsagainst the properties you mapped in the previous step. These assertion functions are listed under the TESTING folder in the Functions view; in content assist, search for them using theassertkeyword instead. An assertion function throws an exception when its check fails, which halts the flow before any later nodes run. - Finish — End the success chain with a Map node that sets the
successoutput property totrue. Because this node only runs if every prior node — including all assertions — completed without throwing,successmust default tofalseso a thrown assertion or an unexpected crash correctly leaves the test reported as failed. This Map node must only ever sit on the successful path; never connect it to a "finally" or cleanup path, since that would let the test report a false-positive pass even when an assertion failed. - (Optional) Teardown — If your test needs cleanup, such as deleting the test data it created, wrap the SUT
invocation (and setup, if relevant) in a Try/Catch/Finally node and connect
your teardown nodes to its
finallyedge handle so they run whether the test passes or fails.
The following diagram illustrates a typical workflow test's node graph, including the optional setup and teardown steps:
flowchart LR
Start([Start Trigger]) --> TryCatch[Try/Catch/Finally]
TryCatch -->|try| Setup[Setup<br/>optional]
Setup --> Invoke[Invoke SUT<br/>Invoke Workflow / Service / Function]
Invoke -->|success| Assert1[Assert<br/>Invoke Function]
Assert1 --> Assert2[Assert<br/>Invoke Function]
Assert2 --> Finish[Map: success = true]
TryCatch -.->|finally| Teardown[Teardown<br/>optional]
Creating a Workflow Test
You can create a workflow test manually with the workflow-test wizard, or ask the Designer agent to generate one for you.
Using the Workflow-Test Wizard
- In the Navigator, right-click the workflow, service, or Groovy function you want to test.
- Click New > Workflow Test.
- Enter a name for the test. The wizard automatically appends the
Test.workflowsuffix to whatever you enter. - If you started from the wrong artifact, optionally change the System Under Test field by browsing for a different workflow, service, or function.
- Click Create.
Expected result: The wizard generates a workflow test with a success output property, an invoke node
targeting your chosen SUT with output mapping already in place, a sample assertion, and a final Map node that sets
success to true — following the contract described in The Required Test Contract.
The wizard opens the generated test in the Workflow Designer automatically, ready for you to edit.
- Replace the sample assertion with the assertions you actually need, using functions from
io.toro.martini.AssertionMethodsas described above. - Save the workflow, then run it from Test Explorer to confirm it passes.
Using the Designer Agent
Instead of building the test by hand, you can ask the Designer agent in the AI Chat view to generate one for you — describe the workflow, service, or function you want tested, and the agent generates the test file following the same contract as the wizard. The Designer agent can also run the resulting test and help you troubleshoot a failure directly in the chat. For more on running and analyzing tests conversationally, see Running and Analyzing Tests with the Designer Agent.
Helpful Resources
- Testing in Martini Designer — Test Explorer basics: view location, test tree, toolbar actions, and automatic refresh
- Running Tests & Test Results — Run scopes, the Test Results view, stack traces, Navigator decoration, and the Debug configuration picker
- Invoke Workflow Node, Invoke Service Node, and Invoke Function Node — Reference for the node types used to call a system under test
- Try Catch Node — Reference for wrapping teardown logic so it always runs
- Assertion Functions — Reference for
io.toro.martini.AssertionMethods, the functions you call to verify a SUT's output - Community Q&A: Martini Community
Have a question? Post or search it here.