Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Made scenario and feature context items more thread-safe #2629

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions TechTalk.SpecFlow/SpecFlowContext.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;

namespace TechTalk.SpecFlow
{
Expand All @@ -8,7 +8,7 @@ public interface ISpecFlowContext
Exception TestError { get; }
}

public abstract class SpecFlowContext : Dictionary<string, object>, IDisposable
public abstract class SpecFlowContext : ConcurrentDictionary<string, object>, IDisposable
{
public Exception TestError { get; internal set; }

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using BoDi;
using Moq;
using TechTalk.SpecFlow.Infrastructure;
using TechTalk.SpecFlow.Tracing;
using Xunit;

namespace TechTalk.SpecFlow.RuntimeTests.Infrastructure
{
/// <summary>
/// Test class which verifies that the <see cref="ScenarioContext"/> and <see cref="FeatureContext"/> Dictionary is thread-safe.
/// </summary>
public class SpecFlowContextThreadSafetyTests : StepExecutionTestsBase
{
private ContextManager CreateContextManager(IObjectContainer testThreadContainer)
{
return new ContextManager(new Mock<ITestTracer>().Object, testThreadContainer, ContainerBuilderStub);
}

[Fact]
public void Test_Scenario_Context_Thread_Safety()
{
var containerBuilder = new RuntimeTestsContainerBuilder();
var testThreadContainer = containerBuilder.CreateTestThreadContainer(containerBuilder.CreateGlobalContainer(typeof(SpecFlowContextThreadSafetyTests).Assembly));
var contextManager = CreateContextManager(testThreadContainer);
contextManager.InitializeFeatureContext(new FeatureInfo(FeatureLanguage, "", "test feature", null));
contextManager.InitializeScenarioContext(new ScenarioInfo("test scenario", "test_description", null, null));

// setup a list containing unique numbers.
var taskNumbers = new List<int>();
while (taskNumbers.Count < 100) taskNumbers.Add(taskNumbers.Count + 1);

// run a task for each number in the list which sets the ScenarioContext item to this unique number in parallel
Parallel.ForEach(
taskNumbers, (i) =>
{
contextManager.ScenarioContext[$"key_{i}"] = i;
});

// Verify that all of the ScenarioContext items has their expected value.
// This fails with different kind of errors when SpecFlowContext inherits from Dictionary in stead of ConcurrentDictionary.
Assert.All(taskNumbers, (i) =>
{
var contextItem = (int) contextManager.ScenarioContext[$"key_{i}"];
Assert.Equal(contextItem , i);
});
}

[Fact]
public void Test_Feature_Context_Thread_Safety()
{
var containerBuilder = new RuntimeTestsContainerBuilder();
var testThreadContainer = containerBuilder.CreateTestThreadContainer(containerBuilder.CreateGlobalContainer(typeof(SpecFlowContextThreadSafetyTests).Assembly));
var contextManager = CreateContextManager(testThreadContainer);
contextManager.InitializeFeatureContext(new FeatureInfo(FeatureLanguage, "", "test feature", null));
contextManager.InitializeScenarioContext(new ScenarioInfo("test scenario", "test_description", null, null));

// setup a list containing unique numbers.
var taskNumbers = new List<int>();
while (taskNumbers.Count < 100) taskNumbers.Add(taskNumbers.Count + 1);

// run a task for each number in the list which sets the FeatureContext item to this unique number in parallel
Parallel.ForEach(
taskNumbers, (i) =>
{
contextManager.FeatureContext[$"key_{i}"] = i;
});

// Verify that all of the FeatureContext items has their expected value.
// This fails with different kind of errors when SpecFlowContext inherits from Dictionary in stead of ConcurrentDictionary.
Assert.All(taskNumbers, (i) =>
{
var contextItem = (int) contextManager.FeatureContext[$"key_{i}"];
Assert.Equal(contextItem , i);
});
}
}
}
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Changes:
+ Default step definition skeletons are generating cucumber expressions.
+ 'ScenarioInfo.ScenarioAndFeatureTags' has been deprecated in favor of 'ScenarioInfo.CombinedTags'. Now both contain rule tags as well.
+ The interface ISpecFlowOutputHelper has been moved to the TechTalk.SpecFlow namespace (from TechTalk.SpecFlow.Infrastructure).
+ The Dictionary behind ScenarioContext, FeatureContext and ScenarioStepContext to store custom data in is now thread-safe

3.10

Expand Down