diff --git a/src/common/Elsa.Testing.Shared/Activities/TestContainer.cs b/src/common/Elsa.Testing.Shared/Activities/TestContainer.cs
new file mode 100644
index 000000000..b22ae3755
--- /dev/null
+++ b/src/common/Elsa.Testing.Shared/Activities/TestContainer.cs
@@ -0,0 +1,38 @@
+using Elsa.Workflows;
+using Elsa.Workflows.Activities;
+
+namespace Elsa.Testing.Shared.Activities;
+
+///
+/// Concrete implementation of Container for testing that schedules children sequentially.
+///
+public class TestContainer : Container
+{
+ private const string CurrentIndexProperty = "CurrentIndex";
+
+ protected override async ValueTask ScheduleChildrenAsync(ActivityExecutionContext context)
+ {
+ await HandleItemAsync(context);
+ }
+
+ private async ValueTask HandleItemAsync(ActivityExecutionContext context)
+ {
+ var currentIndex = context.GetProperty(CurrentIndexProperty);
+ var childActivities = Activities.ToList();
+
+ if (currentIndex >= childActivities.Count)
+ {
+ await context.CompleteActivityAsync();
+ return;
+ }
+
+ var nextActivity = childActivities.ElementAt(currentIndex);
+ await context.ScheduleActivityAsync(nextActivity, OnChildCompleted);
+ context.UpdateProperty(CurrentIndexProperty, x => x + 1);
+ }
+
+ private async ValueTask OnChildCompleted(ActivityCompletedContext context)
+ {
+ await HandleItemAsync(context.TargetContext);
+ }
+}
diff --git a/test/integration/Elsa.Workflows.IntegrationTests/Activities/Container/ContainerTests.cs b/test/integration/Elsa.Workflows.IntegrationTests/Activities/Container/ContainerTests.cs
new file mode 100644
index 000000000..687958c11
--- /dev/null
+++ b/test/integration/Elsa.Workflows.IntegrationTests/Activities/Container/ContainerTests.cs
@@ -0,0 +1,111 @@
+using Elsa.Testing.Shared;
+using Elsa.Workflows.Activities;
+using Elsa.Workflows.Models;
+using Xunit.Abstractions;
+
+namespace Elsa.Workflows.IntegrationTests.Activities.Container;
+
+///
+/// Integration tests for the activity.
+///
+public class ContainerTests(ITestOutputHelper testOutputHelper)
+{
+ private readonly WorkflowTestFixture _fixture = new(testOutputHelper);
+
+ [Fact(DisplayName = "Container executes child activities in order")]
+ public async Task Container_ExecutesChildActivitiesInOrder()
+ {
+ // Arrange & Act
+ var (result, lines) = await RunWorkflowAndCaptureOutput(new SimpleContainerWorkflow());
+
+ // Assert
+ Assert.Equal(WorkflowStatus.Finished, result.WorkflowState.Status);
+ Assert.Equal(new[] { "Activity 1", "Activity 2", "Activity 3" }, lines);
+ }
+
+ [Fact(DisplayName = "Container with variables makes them available to child activities")]
+ public async Task Container_WithVariables_MakesThemAvailableToChildren()
+ {
+ // Arrange & Act
+ var (result, lines) = await RunWorkflowAndCaptureOutput(new ContainerWithVariablesWorkflow());
+
+ // Assert
+ Assert.Equal(WorkflowStatus.Finished, result.WorkflowState.Status);
+ Assert.Contains("Counter: 0", lines[0]);
+ Assert.Contains("Counter: 1", lines[1]);
+ Assert.Contains("Counter: 2", lines[2]);
+ }
+
+ [Fact(DisplayName = "Container with nested containers scopes variables correctly")]
+ public async Task Container_WithNestedContainers_ScopesVariablesCorrectly()
+ {
+ // Arrange & Act
+ var (result, lines) = await RunWorkflowAndCaptureOutput(new NestedContainersWorkflow());
+
+ // Assert
+ Assert.Equal(WorkflowStatus.Finished, result.WorkflowState.Status);
+ Assert.Contains("Outer: 10", lines[0]);
+ Assert.Contains("Inner: 20", lines[1]);
+ Assert.Contains("Outer again: 10", lines[2]);
+ }
+
+ [Fact(DisplayName = "Container with unnamed variables auto-names them")]
+ public async Task Container_WithUnnamedVariables_AutoNamesThem()
+ {
+ // Arrange & Act
+ var (result, _) = await RunWorkflowAndCaptureOutput(new ContainerWithUnnamedVariablesWorkflow());
+
+ // Assert
+ Assert.Equal(WorkflowStatus.Finished, result.WorkflowState.Status);
+ // The workflow should complete successfully even with unnamed variables
+ }
+
+ [Fact(DisplayName = "Container with no activities completes successfully")]
+ public async Task Container_WithNoActivities_CompletesSuccessfully()
+ {
+ // Arrange & Act
+ var (result, lines) = await RunWorkflowAndCaptureOutput(new EmptyContainerWorkflow());
+
+ // Assert
+ Assert.Equal(WorkflowStatus.Finished, result.WorkflowState.Status);
+ Assert.Empty(lines);
+ }
+
+ [Fact(DisplayName = "Container with multiple variable types handles them correctly")]
+ public async Task Container_WithMultipleVariableTypes_HandlesThemCorrectly()
+ {
+ // Arrange & Act
+ var (result, lines) = await RunWorkflowAndCaptureOutput(new ContainerWithMixedVariableTypesWorkflow());
+
+ // Assert
+ Assert.Equal(WorkflowStatus.Finished, result.WorkflowState.Status);
+ Assert.Contains("Int: 42", lines[0]);
+ Assert.Contains("String: Hello", lines[1]);
+ Assert.Contains("Bool: True", lines[2]);
+ }
+
+ [Theory(DisplayName = "Container handles different numbers of child activities")]
+ [InlineData(1)]
+ [InlineData(5)]
+ [InlineData(10)]
+ public async Task Container_HandlesDifferentNumbersOfChildActivities(int activityCount)
+ {
+ // Arrange & Act
+ var (result, lines) = await RunWorkflowAndCaptureOutput(new DynamicContainerWorkflow(activityCount));
+
+ // Assert
+ Assert.Equal(WorkflowStatus.Finished, result.WorkflowState.Status);
+ Assert.Equal(activityCount, lines.Count);
+ for (var i = 0; i < activityCount; i++)
+ {
+ Assert.Equal($"Activity {i + 1}", lines[i]);
+ }
+ }
+
+ private async Task<(RunWorkflowResult Result, List Lines)> RunWorkflowAndCaptureOutput(IWorkflow workflow)
+ {
+ var result = await _fixture.RunWorkflowAsync(workflow);
+ var lines = _fixture.CapturingTextWriter.Lines.ToList();
+ return (result, lines);
+ }
+}
diff --git a/test/integration/Elsa.Workflows.IntegrationTests/Activities/Container/Workflows.cs b/test/integration/Elsa.Workflows.IntegrationTests/Activities/Container/Workflows.cs
new file mode 100644
index 000000000..d9215d94a
--- /dev/null
+++ b/test/integration/Elsa.Workflows.IntegrationTests/Activities/Container/Workflows.cs
@@ -0,0 +1,154 @@
+using Elsa.Testing.Shared.Activities;
+using Elsa.Workflows.Activities;
+using Elsa.Workflows.Memory;
+
+namespace Elsa.Workflows.IntegrationTests.Activities.Container;
+
+///
+/// Simple workflow with a container that executes activities sequentially.
+///
+public class SimpleContainerWorkflow : WorkflowBase
+{
+ protected override void Build(IWorkflowBuilder workflow)
+ {
+ workflow.Root = new TestContainer
+ {
+ Activities =
+ {
+ new WriteLine("Activity 1"),
+ new WriteLine("Activity 2"),
+ new WriteLine("Activity 3")
+ }
+ };
+ }
+}
+
+///
+/// Workflow demonstrating container with variables.
+///
+public class ContainerWithVariablesWorkflow : WorkflowBase
+{
+ protected override void Build(IWorkflowBuilder workflow)
+ {
+ var counter = new Variable("Counter", 0);
+
+ workflow.Root = new TestContainer
+ {
+ Variables = { counter },
+ Activities =
+ {
+ new WriteLine(context => $"Counter: {counter.Get(context)}"),
+ new SetVariable { Variable = counter, Value = new(1) },
+ new WriteLine(context => $"Counter: {counter.Get(context)}"),
+ new SetVariable { Variable = counter, Value = new(2) },
+ new WriteLine(context => $"Counter: {counter.Get(context)}")
+ }
+ };
+ }
+}
+
+///
+/// Workflow with nested containers to test variable scoping.
+///
+public class NestedContainersWorkflow : WorkflowBase
+{
+ protected override void Build(IWorkflowBuilder workflow)
+ {
+ var outerVar = new Variable("OuterVar", 10);
+ var innerVar = new Variable("InnerVar", 20);
+
+ workflow.Root = new TestContainer
+ {
+ Variables = { outerVar },
+ Activities =
+ {
+ new WriteLine(context => $"Outer: {outerVar.Get(context)}"),
+ new TestContainer
+ {
+ Variables = { innerVar },
+ Activities =
+ {
+ new WriteLine(context => $"Inner: {innerVar.Get(context)}")
+ }
+ },
+ new WriteLine(context => $"Outer again: {outerVar.Get(context)}")
+ }
+ };
+ }
+}
+
+///
+/// Workflow with unnamed variables to test auto-naming.
+///
+public class ContainerWithUnnamedVariablesWorkflow : WorkflowBase
+{
+ protected override void Build(IWorkflowBuilder workflow)
+ {
+ workflow.Root = new TestContainer
+ {
+ Variables =
+ {
+ new Variable(),
+ new Variable(),
+ new Variable()
+ },
+ Activities =
+ {
+ new WriteLine("Unnamed variables workflow")
+ }
+ };
+ }
+}
+
+///
+/// Workflow with an empty container (no activities).
+///
+public class EmptyContainerWorkflow : WorkflowBase
+{
+ protected override void Build(IWorkflowBuilder workflow)
+ {
+ workflow.Root = new TestContainer();
+ }
+}
+
+///
+/// Workflow with mixed variable types.
+///
+public class ContainerWithMixedVariableTypesWorkflow : WorkflowBase
+{
+ protected override void Build(IWorkflowBuilder workflow)
+ {
+ var intVar = new Variable("IntVar", 42);
+ var stringVar = new Variable("StringVar", "Hello");
+ var boolVar = new Variable("BoolVar", true);
+
+ workflow.Root = new TestContainer
+ {
+ Variables = { intVar, stringVar, boolVar },
+ Activities =
+ {
+ new WriteLine(context => $"Int: {intVar.Get(context)}"),
+ new WriteLine(context => $"String: {stringVar.Get(context)}"),
+ new WriteLine(context => $"Bool: {boolVar.Get(context)}")
+ }
+ };
+ }
+}
+
+///
+/// Workflow with a dynamic number of activities.
+///
+public class DynamicContainerWorkflow(int activityCount) : WorkflowBase
+{
+ protected override void Build(IWorkflowBuilder workflow)
+ {
+ var container = new TestContainer();
+
+ for (var i = 1; i <= activityCount; i++)
+ {
+ container.Activities.Add(new WriteLine($"Activity {i}"));
+ }
+
+ workflow.Root = container;
+ }
+}
diff --git a/test/unit/Elsa.Activities.UnitTests/Composition/ContainerTests.cs b/test/unit/Elsa.Activities.UnitTests/Composition/ContainerTests.cs
new file mode 100644
index 000000000..f0a145e10
--- /dev/null
+++ b/test/unit/Elsa.Activities.UnitTests/Composition/ContainerTests.cs
@@ -0,0 +1,217 @@
+using Elsa.Testing.Shared;
+using Elsa.Testing.Shared.Activities;
+using Elsa.Workflows;
+using NSubstitute;
+
+namespace Elsa.Activities.UnitTests.Composition;
+
+///
+/// Unit tests for the activity focusing on ExecuteAsync behavior.
+///
+public class ContainerTests
+{
+ [Theory(DisplayName = "Container schedules child activities")]
+ [InlineData(1)]
+ [InlineData(3)]
+ [InlineData(5)]
+ public async Task Container_SchedulesChildActivities(int activityCount)
+ {
+ // Arrange
+ var activities = CreateActivities(activityCount);
+ var container = CreateContainerWithActivities(activities);
+
+ // Act
+ var context = await ExecuteContainerAsync(container);
+
+ // Assert - First activity should be scheduled (sequential scheduling)
+ Assert.True(context.HasScheduledActivity(activities[0]));
+ }
+
+ [Fact(DisplayName = "Container with no activities completes without scheduling")]
+ public async Task Container_WithNoActivities_CompletesWithoutScheduling()
+ {
+ // Arrange
+ var container = new TestContainer();
+
+ // Act
+ var context = await ExecuteContainerAsync(container);
+
+ // Assert
+ var scheduledActivities = context.WorkflowExecutionContext.Scheduler.List().ToList();
+ Assert.Empty(scheduledActivities);
+ }
+
+ [Fact(DisplayName = "Container declares variables in memory")]
+ public async Task Container_DeclaresVariablesInMemory()
+ {
+ // Arrange
+ var variable1 = new Variable("Counter", 10);
+ var variable2 = new Variable("Name", "Test");
+ var container = CreateContainerWithVariables(variable1, variable2);
+
+ // Act
+ var context = await ExecuteContainerAsync(container);
+
+ // Assert
+ var memory = context.ExpressionExecutionContext.Memory;
+ Assert.True(memory.HasBlock(variable1.Id));
+ Assert.True(memory.HasBlock(variable2.Id));
+ }
+
+ [Fact(DisplayName = "Container auto-names unnamed variables")]
+ public async Task Container_AutoNamesUnnamedVariables()
+ {
+ // Arrange
+ var var1 = new Variable();
+ var var2 = new Variable();
+ var var3 = new Variable();
+ var container = CreateContainerWithVariables(var1, var2, var3);
+
+ // Act
+ await ExecuteContainerAsync(container);
+
+ // Assert
+ Assert.Equal("Variable1", var1.Name);
+ Assert.Equal("Variable2", var2.Name);
+ Assert.Equal("Variable3", var3.Name);
+ }
+
+ [Fact(DisplayName = "Container preserves named variables")]
+ public async Task Container_PreservesNamedVariables()
+ {
+ // Arrange
+ var namedVar = new Variable("MyVariable", "value");
+ var unnamedVar = new Variable();
+ var container = CreateContainerWithVariables(namedVar, unnamedVar);
+
+ // Act
+ await ExecuteContainerAsync(container);
+
+ // Assert
+ Assert.Equal("MyVariable", namedVar.Name);
+ Assert.Equal("Variable1", unnamedVar.Name);
+ }
+
+ [Theory(DisplayName = "Container handles multiple unnamed variables correctly")]
+ [InlineData(1)]
+ [InlineData(5)]
+ [InlineData(10)]
+ public async Task Container_HandlesMultipleUnnamedVariables(int variableCount)
+ {
+ // Arrange
+ var variables = CreateUnnamedVariables(variableCount);
+ var container = CreateContainerWithVariables(variables.Cast().ToArray());
+
+ // Act
+ await ExecuteContainerAsync(container);
+
+ // Assert
+ for (var i = 0; i < variableCount; i++)
+ {
+ Assert.Equal($"Variable{i + 1}", variables[i].Name);
+ }
+ }
+
+ [Fact(DisplayName = "Container with mixed named and unnamed variables")]
+ public async Task Container_WithMixedNamedAndUnnamedVariables()
+ {
+ // Arrange
+ var namedVar1 = new Variable("FirstVar", 1);
+ var unnamedVar1 = new Variable();
+ var namedVar2 = new Variable("SecondVar", "test");
+ var unnamedVar2 = new Variable();
+ var container = CreateContainerWithVariables(namedVar1, unnamedVar1, namedVar2, unnamedVar2);
+
+ // Act
+ await ExecuteContainerAsync(container);
+
+ // Assert
+ Assert.Equal("FirstVar", namedVar1.Name);
+ Assert.Equal("Variable1", unnamedVar1.Name);
+ Assert.Equal("SecondVar", namedVar2.Name);
+ Assert.Equal("Variable2", unnamedVar2.Name);
+ }
+
+ [Fact(DisplayName = "Container schedules mixed activity types")]
+ public async Task Container_SchedulesMixedActivityTypes()
+ {
+ // Arrange
+ var writeLine = new WriteLine("Test output");
+ var testVar = new Variable("testVar", 0, "testVar");
+ var setVariable = new SetVariable(testVar, new Input(42));
+ var mockActivity = Substitute.For();
+ var container = CreateContainerWithActivities(writeLine, setVariable, mockActivity);
+
+ // Act
+ var context = await ExecuteContainerAsync(container);
+
+ // Assert
+ Assert.True(context.HasScheduledActivity(writeLine));
+ }
+
+ [Fact(DisplayName = "Container with single activity schedules it")]
+ public async Task Container_WithSingleActivity_SchedulesIt()
+ {
+ // Arrange
+ var activity = new WriteLine("Single activity");
+ var container = CreateContainerWithActivities(activity);
+
+ // Act
+ var context = await ExecuteContainerAsync(container);
+
+ // Assert
+ Assert.True(context.HasScheduledActivity(activity));
+ }
+
+ [Fact(DisplayName = "Container with variables and activities works correctly")]
+ public async Task Container_WithVariablesAndActivities_WorksCorrectly()
+ {
+ // Arrange
+ var variable = new Variable("Counter", 0);
+ var activity = new WriteLine("Activity");
+ var container = new TestContainer();
+ container.Variables.Add(variable);
+ container.Activities.Add(activity);
+
+ // Act
+ var context = await ExecuteContainerAsync(container);
+
+ // Assert
+ Assert.True(context.ExpressionExecutionContext.Memory.HasBlock(variable.Id));
+ Assert.True(context.HasScheduledActivity(activity));
+ }
+
+ private static Task ExecuteContainerAsync(TestContainer container) =>
+ new ActivityTestFixture(container).ExecuteAsync();
+
+ private static IActivity[] CreateActivities(int count) =>
+ Enumerable.Range(1, count)
+ .Select(i => new WriteLine($"Activity {i}"))
+ .Cast()
+ .ToArray();
+
+ private static List> CreateUnnamedVariables(int count) =>
+ Enumerable.Range(0, count)
+ .Select(_ => new Variable())
+ .ToList();
+
+ private static TestContainer CreateContainerWithActivities(params IActivity[] activities)
+ {
+ var container = new TestContainer();
+ foreach (var activity in activities)
+ {
+ container.Activities.Add(activity);
+ }
+ return container;
+ }
+
+ private static TestContainer CreateContainerWithVariables(params Variable[] variables)
+ {
+ var container = new TestContainer();
+ foreach (var variable in variables)
+ {
+ container.Variables.Add(variable);
+ }
+ return container;
+ }
+}