并行运行specflow场景多次

时间:2016-12-07 15:33:48

标签: c# bdd specflow

我遇到的情况是我尝试多次并行执行相同的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();
    }

有人能指出我在这个方向上正确的方向吗?

0 个答案:

没有答案