diff --git a/doc/qa/test-guidelines.md b/doc/qa/test-guidelines.md
index de0b29cc1..81feb0519 100644
--- a/doc/qa/test-guidelines.md
+++ b/doc/qa/test-guidelines.md
@@ -160,6 +160,42 @@ public async Task Should_Schedule_Child_Activity()
}
```
+**Example (checking activity outcomes):**
+```csharp
+[Fact]
+public async Task Should_Return_Multiple_Outcomes()
+{
+ // Arrange
+ var flowFork = new FlowFork
+ {
+ Branches = new(new[] { "Branch1", "Branch2", "Branch3" })
+ };
+
+ // Act
+ var context = await new ActivityTestFixture(flowFork).ExecuteAsync();
+
+ // Assert - Check all outcomes
+ var outcomes = context.GetOutcomes().ToList();
+ Assert.Equal(3, outcomes.Count);
+ Assert.Contains("Branch1", outcomes);
+ Assert.Contains("Branch2", outcomes);
+ Assert.Contains("Branch3", outcomes);
+}
+
+[Fact]
+public async Task Should_Return_Default_Outcome()
+{
+ // Arrange
+ var flowSwitch = new FlowSwitch();
+
+ // Act
+ var context = await new ActivityTestFixture(flowSwitch).ExecuteAsync();
+
+ // Assert - Check single outcome
+ Assert.True(context.HasOutcome("Default"));
+}
+```
+
#### **Integration tests:**
- Place the activity inside a minimal workflow definition and run via [`IWorkflowRunner.RunAsync`](../../src/modules/Elsa.Workflows.Core/Contracts/IWorkflowRunner.cs). Assert outputs/variables and that the activity integrates correctly with preceding/following activities.
- If activity creates bookmarks or relies on scheduler semantics, integration tests should resume bookmarks via the engine APIs to validate resumption.
@@ -214,6 +250,8 @@ Assert.Equal(WorkflowStatus.Finished, resumed.WorkflowInstance.Status);
| `ActivityTestFixture.ExecuteAsync` | Execute activity and return context | Standard activity unit test execution |
| `context.GetActivityOutput` | Get output from activity using expression selector | Asserting activity outputs in unit tests |
| `context.HasScheduledActivity` | Check if activity is scheduled | Verifying scheduling behavior in unit tests |
+| `context.GetOutcomes` | Get all outcomes from activity execution | Asserting multiple outcomes in unit tests |
+| `context.HasOutcome` | Check if activity has specific outcome | Asserting single outcome in unit tests |
| `IWorkflowRunner.RunAsync` | Execute workflow in-process | Integration / Component tests |
| `RunActivityExtensions.RunActivityAsync` | Run single activity as workflow | Integration tests for single activities |
| `PopulateRegistriesAsync` | Register types for JSON deserialization | Loading JSON workflows.
Integration tests only |
diff --git a/src/common/Elsa.Testing.Shared/ActivityExecutionContextExtensions.cs b/src/common/Elsa.Testing.Shared/ActivityExecutionContextExtensions.cs
index 6af31b080..3f9d03d9d 100644
--- a/src/common/Elsa.Testing.Shared/ActivityExecutionContextExtensions.cs
+++ b/src/common/Elsa.Testing.Shared/ActivityExecutionContextExtensions.cs
@@ -17,14 +17,23 @@ public static class ActivityExecutionContextExtensions
{
return activityExecutionContext.WorkflowExecutionContext.Scheduler.Find(x => x.Activity == activity) != null;
}
-
+
+ ///
+ /// Retrieves the collection of outcomes recorded in the execution context's journal data.
+ ///
+ /// The activity execution context from which to retrieve the outcomes.
+ /// A collection of outcome strings, or an empty collection if no outcomes are present.
public static IEnumerable GetOutcomes(this ActivityExecutionContext activityExecutionContext)
{
- if (activityExecutionContext.JournalData.TryGetValue("Outcomes", out var outcomesObj) && outcomesObj is string[] outcomes)
- return outcomes;
- return [];
+ return activityExecutionContext.JournalData.TryGetValue("Outcomes", out var outcomes) && outcomes is string[] arr ? arr : [];
}
-
+
+ ///
+ /// Determines whether the specified outcome exists in the current activity execution context.
+ ///
+ /// The activity execution context to check for the outcome.
+ /// The outcome to verify within the execution context.
+ /// True if the specified outcome exists; otherwise, false.
public static bool HasOutcome(this ActivityExecutionContext activityExecutionContext, string outcome)
{
return activityExecutionContext.GetOutcomes().Contains(outcome);
diff --git a/test/unit/Elsa.Activities.UnitTests/Branching/FlowSwitchTests.cs b/test/unit/Elsa.Activities.UnitTests/Branching/FlowSwitchTests.cs
new file mode 100644
index 000000000..bbaff3172
--- /dev/null
+++ b/test/unit/Elsa.Activities.UnitTests/Branching/FlowSwitchTests.cs
@@ -0,0 +1,99 @@
+using Elsa.Expressions.Models;
+using Elsa.Testing.Shared;
+using Elsa.Workflows;
+using Elsa.Workflows.Activities.Flowchart.Activities;
+using Elsa.Workflows.Activities.Flowchart.Models;
+
+namespace Elsa.Activities.UnitTests.Branching;
+
+public class FlowSwitchTests
+{
+ [Theory]
+ [MemberData(nameof(DefaultOutcomeTestCases))]
+ public async Task Should_Return_Default_When_No_Cases_Match(List cases)
+ {
+ // Arrange
+ var flowSwitch = new FlowSwitch { Cases = cases };
+
+ // Act
+ var context = await ExecuteAsync(flowSwitch);
+
+ // Assert - Activity should return Default outcome when no cases match.
+ Assert.True(context.HasOutcome("Default"));
+ }
+
+ [Theory]
+ [MemberData(nameof(SwitchModeTestCases))]
+ public async Task Should_Return_Outcomes_Based_On_Switch_Mode(
+ List cases,
+ SwitchMode mode,
+ string[] expectedOutcomes)
+ {
+ // Arrange
+ var flowSwitch = new FlowSwitch
+ {
+ Cases = cases,
+ Mode = new(mode)
+ };
+
+ // Act
+ var context = await ExecuteAsync(flowSwitch);
+
+ // Assert - Activity should return outcomes based on switch mode.
+ var outcomes = context.GetOutcomes().ToList();
+ Assert.Equal(expectedOutcomes.Length, outcomes.Count);
+ foreach (var expectedOutcome in expectedOutcomes)
+ Assert.Contains(expectedOutcome, outcomes);
+ }
+
+ [Fact]
+ public async Task Should_Evaluate_Cases_With_Literal_Expression()
+ {
+ // Arrange
+ var flowSwitch = new FlowSwitch
+ {
+ Cases = new List
+ {
+ new("TrueCase", Expression.LiteralExpression(true)),
+ new("FalseCase", Expression.LiteralExpression(false))
+ }
+ };
+
+ // Act
+ var context = await ExecuteAsync(flowSwitch);
+
+ // Assert - Activity should match the case with true literal.
+ Assert.True(context.HasOutcome("TrueCase"));
+ }
+
+ public static IEnumerable