Adds unit tests for FlowSwitch activity (#6996)
* Add unit tests for `FlowFork` activity and enhance test utilities - Introduce `FlowForkTests` to validate activity behavior with various branch configurations. - Extend `ActivityExecutionContextExtensions` with methods to retrieve and check outcomes. - Modify `ActivityTestFixture` to ensure activities transition to `Running` status before execution. * Add unit tests for `FlowSwitch` activity and extend test utilities - Implement `FlowSwitchTests` to verify behavior based on switch cases, modes, and literal expressions. - Enhance `ActivityExecutionContextExtensions` with methods to retrieve and check outcomes in execution context. * Update test guidelines to include examples for checking activity outcomes - Add unit test examples for validating multiple and default outcomes. - Extend documentation to describe new `ActivityExecutionContextExtensions` methods: `GetOutcomes` and `HasOutcome`. * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Simplify `GetOutcomes` method in `ActivityExecutionContextExtensions`. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
f7b7ea4d73
commit
d0a4520e7d
|
|
@ -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. <br/>Integration tests only |
|
||||
|
|
|
|||
|
|
@ -17,14 +17,23 @@ public static class ActivityExecutionContextExtensions
|
|||
{
|
||||
return activityExecutionContext.WorkflowExecutionContext.Scheduler.Find(x => x.Activity == activity) != null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the collection of outcomes recorded in the execution context's journal data.
|
||||
/// </summary>
|
||||
/// <param name="activityExecutionContext">The activity execution context from which to retrieve the outcomes.</param>
|
||||
/// <returns>A collection of outcome strings, or an empty collection if no outcomes are present.</returns>
|
||||
public static IEnumerable<string> 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 : [];
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified outcome exists in the current activity execution context.
|
||||
/// </summary>
|
||||
/// <param name="activityExecutionContext">The activity execution context to check for the outcome.</param>
|
||||
/// <param name="outcome">The outcome to verify within the execution context.</param>
|
||||
/// <returns>True if the specified outcome exists; otherwise, false.</returns>
|
||||
public static bool HasOutcome(this ActivityExecutionContext activityExecutionContext, string outcome)
|
||||
{
|
||||
return activityExecutionContext.GetOutcomes().Contains(outcome);
|
||||
|
|
|
|||
|
|
@ -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<FlowSwitchCase> 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<FlowSwitchCase> 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<FlowSwitchCase>
|
||||
{
|
||||
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<object[]> DefaultOutcomeTestCases()
|
||||
{
|
||||
yield return [new List<FlowSwitchCase>()];
|
||||
yield return
|
||||
[
|
||||
new List<FlowSwitchCase>
|
||||
{
|
||||
new("Case1", () => false),
|
||||
new("Case2", () => false)
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> SwitchModeTestCases()
|
||||
{
|
||||
var cases = new List<FlowSwitchCase>
|
||||
{
|
||||
new("Case1", () => false),
|
||||
new("Case2", () => true),
|
||||
new("Case3", () => true)
|
||||
};
|
||||
|
||||
yield return [cases, SwitchMode.MatchFirst, new[] { "Case2" }];
|
||||
yield return [cases, SwitchMode.MatchAny, new[] { "Case2", "Case3" }];
|
||||
}
|
||||
|
||||
private static Task<ActivityExecutionContext> ExecuteAsync(IActivity activity)
|
||||
{
|
||||
return new ActivityTestFixture(activity).ExecuteAsync();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue