我遇到的情况是我尝试多次并行执行相同的specflow场景,但是当我运行它时会导致一些错误(间歇性表明竞争条件)
错误是
BoDi.ObjectContainerException:Object container disposed
at BoDi.ObjectContainer.AssertNotDisposed()
at BoDi.ObjectContainer.Resolve(Type typeToResolve, ResolutionList resolutionPath, String name)
at BoDi.ObjectContainer.Resolve(Type typeToResolve, String name)
at TechTalk.SpecFlow.Infrastructure.BindingInstanceResolver.ResolveBindingInstance(Type bindingType, IObjectContainer scenarioContainer)
at lambda_method(Closure , IContextManager , Int32 , Int32 , Int32 )
at TechTalk.SpecFlow.Bindings.BindingInvoker.InvokeBinding(IBinding binding, IContextManager contextManager, Object[] arguments, ITestTracer testTracer, TimeSpan& duration)
at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.ExecuteStepMatch(BindingMatch match, Object[] arguments)
at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.ExecuteStep(StepInstance stepInstance)
at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.OnAfterLastStep()
at TechTalk.SpecFlow.TestRunner.CollectScenarioErrors()
at LoadFeature.ScenarioCleanup()
at LoadFeature.ScenarioCleanup()
at LoadFeature.AddTwoNumbersTogether(String number1, String number2, String answer, String runNumber, String[] exampleTags) in ...
和
System.Argument Exception: An item with the same key has already been added
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at BoDi.ObjectContainer.TypeRegistration.Resolve(ObjectContainer container, RegistrationKey keyToResolve, ResolutionList resolutionPath)
at BoDi.ObjectContainer.ResolveObject(RegistrationKey keyToResolve, ResolutionList resolutionPath)
at BoDi.ObjectContainer.Resolve(Type typeToResolve, ResolutionList resolutionPath, String name)
at BoDi.ObjectContainer.Resolve(Type typeToResolve, String name)
at TechTalk.SpecFlow.Infrastructure.BindingInstanceResolver.ResolveBindingInstance(Type bindingType, IObjectContainer scenarioContainer)
at lambda_method(Closure , IContextManager , Int32 , Int32 , Int32 )
at TechTalk.SpecFlow.Bindings.BindingInvoker.InvokeBinding(IBinding binding, IContextManager contextManager, Object[] arguments, ITestTracer testTracer, TimeSpan& duration)
at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.ExecuteStepMatch(BindingMatch match, Object[] arguments)
at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.ExecuteStep(StepInstance stepInstance)
at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.OnAfterLastStep()
at TechTalk.SpecFlow.TestRunner.CollectScenarioErrors()
at LoadFeature.ScenarioCleanup()
产生此代码的代码是:
Scenario Outline: Add two numbers together
Given numbers <number1> and <number2> for run <runNumber>
When I add them together for run <runNumber>
Then I get <answer> for run <runNumber>
Scenarios:
| number1 | number2 | answer | runNumber |
| 1 | 2 | 3 | 1 |
| 2 | 2 | 4 | 2 |
Scenario Outline: Load test addy ups parallel
When I execute scenario 'Add two numbers together' in parallel <load> times with parameters <parameters>
Then All is well
Scenarios:
| load | service |parameters |
| 5 | 'devenv' |'1,2,3,x' |
[Binding]
public class LoadSteps
{
private static ConcurrentDictionary<long, RunData> runParameters;
private static ConcurrentDictionary<long, int> _results;
static LoadSteps()
{
runParameters = new ConcurrentDictionary<long, RunData>();
_results = new ConcurrentDictionary<long, int>();
}
[Given(@"numbers (.*) and (.*) for run (.*)")]
public void GivenNumbersAnd(int p0, int p1, int runNumber)
{
runParameters.AddOrUpdate(runNumber, new RunData { Number1 = p0, Number2 = p1 }, (key, value) => new RunData { Number1 = p0, Number2 = p1 });
}
[When(@"I add them together for run (.*)")]
public void WhenIAddThemTogether(int runNumber)
{
if (!runParameters.ContainsKey(runNumber))
{
Debug.WriteLine($"runParameters do not contain the required key {runNumber}, only contains {string.Join(",", runParameters.Keys)}");
}
else
{
var result = runParameters[runNumber].Number1 + runParameters[runNumber].Number2;
_results.AddOrUpdate(runNumber, result, (k,v) => result);
}
}
[When(@"I execute scenario '(.*)' in parallel (.*) times with parameters '(.*)'")]
public void WhenIExecuteScenarioInParallelTimes(string p0, int p1, string p2)
{
var parameters = p2.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Cast<object>().Concat(new object[] { (string[])null }).ToArray();
var feature = new LoadFeature();
var po = new ParallelOptions { MaxDegreeOfParallelism = p1 };
Parallel.ForEach(Enumerable.Range(0, p1), x =>
{
Console.WriteLine(x);
var scenario = FindScenario<TelephonyLoadFeature>(p0);
var parameters1 = new object[parameters.Length];
Array.Copy(parameters, parameters1, parameters.Length);
parameters1[parameters.Length - 2] = x.ToString();
scenario.Invoke(feature, parameters1);
Console.WriteLine($"{x} finished");
});
}
[Then(@"I get (.*) for run (.*)")]
public void ThenIGet(int p0, int runNumber)
{
if (!_results.ContainsKey(runNumber))
{
Debug.WriteLine($"results do not contain the required key {runNumber}, only contains {string.Join(",", _results.Keys)}");
}
else
{
Assert.AreEqual(p0, _results[runNumber]);
}
}
[Then(@"All is well")]
public void ThenAllIsWell()
{
Assert.IsTrue(true);
}
private static MethodInfo FindScenario<T>(string p0)
where T : new()
{
var scenario = typeof(T).GetMethods().FirstOrDefault(y => y.Name.Equals(p0.Replace(" ", string.Empty), System.StringComparison.OrdinalIgnoreCase));
if (scenario == null)
{
ScenarioContext.Current.Pending();
}
return scenario;
}
private struct RunData
{
public int Number1 { get; set; }
public int Number2 { get; set; }
}
}
请注意,按顺序多次运行基本方案可以正常运行
Scenario Outline: Load test addy ups sequential
When I execute scenario 'Add two numbers together' in sequence <load> times with parameters <parameters>
Then All is well
Scenarios:
| load | service | parameters |
| 500 | 'devenv' | '1,2,3,x' |
[When(@"I execute scenario '(.*)' in sequence (.*) times with parameters '(.*)'")]
public void WhenIExecuteScenarioInSequenceTimesWithParameters(string p0, int p1, string p2)
{
var parameters = p2.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Cast<object>().Concat(new object[] { (string[])null }).ToArray();
var feature = new LoadFeature();
Enumerable.Range(0, p1).Select(x =>
{
var scenario = FindScenario<TelephonyLoadFeature>(p0);
var parameters1 = new object[parameters.Length];
Array.Copy(parameters, parameters1, parameters.Length);
parameters1[parameters.Length - 2] = x.ToString();
scenario.Invoke(feature, parameters1);
return x;
}).ToArray();
}
有人能指出我在这个方向上正确的方向吗?