Fluent Commerce Logo
Docs
Sign In

Workflow Executor

Essential knowledge

Author:

Kirill Gaiduk

Changed on:

5 Sept 2025

Overview

While the `RuleExecutor` is perfect for unit testing a single Rule, the `WorkflowExecutor` is the tool of choice for integration testing an entire Workflow or parts of it. It reads a Workflow definition from a JSON file and simulates the Fluent Orchestration Engine by executing the sequence of Rules and Rulesets.

Key points

  • Integration Testing Focus`WorkflowExecutor` is the primary tool for integration testing an entire or parts of a Workflow.
  • Workflow Simulation: It mimics the Fluent Commerce Orchestration Engine by processing an initial Event and then passing the resulting Event to the next Ruleset in the chain.
  • Rule Mocking: The `.mockRule()` method is a powerful feature that allows you to replace a real Rule in the Workflow with a mock implementation. This is crucial for isolating the part of the Workflow you want to test.
  • Targeted API Mocking: The `.mockNamedQuery()` method lets you mock a GraphQL query only when it is called by a specific Rule, giving you fine-grained control over test data for different stages of the Workflow.
  • Consolidated Assertions: The `execute` method returns a single Context containing a consolidated list of all Actions from all Rules, allowing you to assert the final state.

Core Methods

The `WorkflowExecutor` builds upon the capabilities of the previous `TestExecutor` and adds several new features:

`of`

This method automatically loads Workflows from a JSON file and includes Rules within the project.

1WorkflowExecutor workflowExecutor = WorkflowExecutor.of("workflows/basic-order.json");
`mockRule`

The `WorkflowExecutor` allows you to simulate Rule executions by specifying mock behaviors for Rules based on their names, bypassing the need for actual Rule execution.

In other words, you can instruct the `WorkflowExecutor`: “Whenever a Rule with this name is encountered, skip the actual execution and simply assume it produced this Action.”

1workflowExecutor.mockRule("SendEvent", c -> c.v2().action().sendEvent(
2	Event.builder().name((String) c.getRule().getProps().get("eventName")).build()
3));
`mockNamedQuery`

In order to define specific mock API responses for each Rule identified by name you can use this method.

1workflowExecutor.mockNamedQuery("ValidateOrder", 
2	GetOrderWithItemsAndFulfilmentsQuery.class, 
3	"graphql/GetOrderWithItemsAndFulfilments/one-item_none-fulfilled.json")

After running the `WorkflowExecutor`, it returns a `RuleContextGenerator` that you can use to assert the expected outcomes of the Workflow execution.

1RuleContextGenerator context = WorkflowExecutor.of("workflows/basic-order.json")
2    .mockRule(
3        "SendEvent", c -> c.context().action().sendEvent(
4            Event.builder().name((String) c.getRule().getProps().get("eventName")).build()))
5    .mockNamedQuery("ValidateOrder", 
6        GetOrderWithItemsAndFulfilmentsQuery.class, 
7        "graphql/GetOrderResponse.json")
8    .execute(
9        Event.builder().name("CREATE").build());
10assertEquals(2, context.getActionsOfType(TestActions.SendEventAction.class).size());
11assertEquals("ValidateOrder", context.getLastActionOfType(TestActions.SendEventAction.class).getEvent().getName());
`ignoreRules`

In order to ignore a specific set of rules during test execution, use the `WorkflowExecutor.ignoreRules` method.

1WorkflowExecutor.ignoreRules(
2    "TEST.mock.LogEvent",
3    "TEST.mock.SendWebhook"
4)
`execute`

Use the `WorkflowExecutor.execute` method to execute the workflow and return the `TestContext` with any produced actions. This method runs the workflow with a default event that has no attributes and references an order entity.

1TestContext testContext = WorkflowExecutor.execute()
`execute` (event)

Use the `WorkflowExecutor.execute` method with an event parameter to execute the workflow with a specific Event and return the `TestContext` with any actions that were produced. This version allows you to run the rule with custom event attributes and/or entity details.

1Event createEvent = Event.builder()
2    .name("CREATE")
3    .entityRef("order-123")
4    .build();
5TestContext testContext = RuleContextGenerator context = executor.execute(createEvent);

Complete Example

Testing with `WorkflowExecutor` is similar to the `RuleExecutor` but is designed to test the interactions between rules.

1. Test Workflow

First, you need a workflow definition file in your `src/test/resources` directory (e.g., `workflows/my_order_workflow.json`). This file defines the sequence of rules that are triggered by specific events.

1{
2  "name": "CREATE",
3  "rules": [
4    {
5      "name": "TEST.custom.ValidateOrder",
6      "props": null
7    },
8    {
9      "name": "TEST.custom.SendOrderWebhookEvent",
10      "props": {
11        "eventName": "SendOrderCreateWebhook"
12      }
13    },
14    {
15      "name": "TEST.core.SendEvent",
16      "props": {
17        "eventName": "RunSourcingLogic"
18      }
19    }
20  ]
21}

2. Test Class

The test class uses the `WorkflowExecutor` to load the workflow, mock any necessary dependencies, and execute it with an initial event.

1import com.fluentcommerce.util.test.executor.WorkflowExecutor;
2import com.fluentcommerce.util.test.executor.RuleContextGenerator;
3import com.fluentretail.rubix.event.Event;
4import org.junit.jupiter.api.Test;
5
6public class MyOrderWorkflowTest {
7
8    @Test
9    void test_FullOrderWorkflow() {
10        // 1. Arrange: Load the workflow file
11        WorkflowExecutor executor = WorkflowExecutor.of("workflows/my_order_workflow.json")
12            // Mock a GraphQL query for a specific rule in the workflow
13            .mockNamedQuery(
14                "TEST.custom.ValidateOrder", 
15                GetOrderByIdQuery.class, 
16                "data/order_to_validate.json"
17            )
18            // Mock a rule entirely. When SendOrderWebhookEvent is called,
19            // just log a message instead of running the real rule.
20            .mockRule(
21                "TEST.custom.SendOrderWebhookEvent",
22                context -> context.context().log("SendOrderWebhookEvent rule was called")
23            );
24
25        // 2. Act: Execute the workflow with an initial event
26        Event initialEvent = Event.builder()
27            .name("CREATE")
28            .entityRef("order-123")
29            .build();
30        RuleContextGenerator context = executor.execute(initialEvent);
31
32        // 3. Assert: Verify the final state
33        // Check the sequence of events that were created
34        assertEquals(1, context.getActionsOfType(SendEventAction.class).size());
35        assertEquals("RunSourcingLogic", context.getLastActionOfType(SendEventAction.class).getEvent().getName());
36        
37        // Check that the mocked rule's log message exists
38        assertEquals(1, context.getActionsOfType(LogAction.class).size());
39        assertTrue(context.getLastActionOfType(LogAction.class).getMessage().contains("SendOrderWebhookEvent rule was called"));
40    }
41}