diff --git a/src/NGherkin.TestAdapter/NGherkinTestExecutor.cs b/src/NGherkin.TestAdapter/NGherkinTestExecutor.cs index 2790383..25dfd06 100644 --- a/src/NGherkin.TestAdapter/NGherkinTestExecutor.cs +++ b/src/NGherkin.TestAdapter/NGherkinTestExecutor.cs @@ -62,73 +62,89 @@ private void RunTest(TestCase testCase, IFrameworkHandle frameworkHandle) using var scope = testCaseExecutionContext.Services.CreateScope(); var gherkinStepRegistrations = scope.ServiceProvider.GetServices(); - foreach (var step in testCaseExecutionContext.Scenario.Steps) + try { - var matchedGherkinStepRegistrations = gherkinStepRegistrations - .Where(x => x.Keyword == step.Keyword.Trim() && x.Pattern.IsMatch(step.Text)) + var stepExecutionContexts = testCaseExecutionContext.Scenario.Steps + .Select(step => GetStepExecutionContext(step, scope.ServiceProvider, gherkinStepRegistrations)) .ToList(); - if (matchedGherkinStepRegistrations.Count == 0) + foreach (var stepExecutionContext in stepExecutionContexts) { - testResult.Outcome = TestOutcome.Failed; - testResult.ErrorMessage = $"Unable to find step for: {step.Keyword.Trim()} {step.Text}"; - frameworkHandle.RecordResult(testResult); - frameworkHandle.RecordEnd(testResult.TestCase, testResult.Outcome); - return; + stepExecutionContext.MethodInfo.Invoke(stepExecutionContext.Target, stepExecutionContext.Arguments); } + } + catch (Exception exception) + { + testResult.Outcome = TestOutcome.Failed; + testResult.ErrorMessage = exception.ToString(); + testResult.ErrorStackTrace = exception.StackTrace; + frameworkHandle.RecordResult(testResult); + frameworkHandle.RecordEnd(testResult.TestCase, testResult.Outcome); + return; + } - if (matchedGherkinStepRegistrations.Count > 1) - { - testResult.Outcome = TestOutcome.Failed; - testResult.ErrorMessage = $"Multiple steps were found for: {step.Keyword.Trim()} {step.Text}"; - frameworkHandle.RecordResult(testResult); - frameworkHandle.RecordEnd(testResult.TestCase, testResult.Outcome); - return; - } + testResult.Outcome = TestOutcome.Passed; + frameworkHandle.RecordResult(testResult); + frameworkHandle.RecordEnd(testResult.TestCase, testResult.Outcome); + } - var gherkinStepRegistration = matchedGherkinStepRegistrations.Single(); - try - { - var targetType = scope.ServiceProvider.GetRequiredService(gherkinStepRegistration.Type); + private StepExecutionContext GetStepExecutionContext( + Step step, + IServiceProvider testScopedServiceProvider, + IEnumerable gherkinStepRegistrations) + { + var matchedGherkinStepRegistrations = gherkinStepRegistrations + .Where(x => x.Keyword == step.Keyword.Trim() && x.Pattern.IsMatch(step.Text)) + .ToList(); - var parameters = gherkinStepRegistration.Method.GetParameters(); + if (matchedGherkinStepRegistrations.Count == 0) + { + throw new Exception($"Unable to find step for: {step.Keyword.Trim()} {step.Text}"); + } - var stepArguments = gherkinStepRegistration.Pattern - .Match(step.Text) - .Groups - .Cast() - .Skip(1) - .Select(x => x.Value) - .ToList(); + if (matchedGherkinStepRegistrations.Count > 1) + { + throw new Exception($"Multiple steps were found for: {step.Keyword.Trim()} {step.Text}"); + } - var expectedParameterCount = step.Argument == null ? stepArguments.Count : stepArguments.Count + 1; - if (expectedParameterCount != parameters.Length) - { - throw new Exception($"Method {gherkinStepRegistration.Type.FullName}.{gherkinStepRegistration.Method.Name} have invalid parameters count"); - } + var gherkinStepRegistration = matchedGherkinStepRegistrations.Single(); + var target = testScopedServiceProvider.GetRequiredService(gherkinStepRegistration.Type); + var arguments = ParseStepArguments(gherkinStepRegistration, step); - var arguments = stepArguments.Select((value, index) => Convert.ChangeType(value, parameters[index].ParameterType)); + return new StepExecutionContext(target, gherkinStepRegistration.Method, arguments); + } - if (step.Argument is DataTable dataTable) - { - arguments = arguments.Concat(new[] { dataTable }); - } + private object[] ParseStepArguments(GherkinStepRegistration gherkinStepRegistration, Step step) + { + var stepTextArguments = gherkinStepRegistration.Pattern + .Match(step.Text) + .Groups + .Cast() + .Skip(1) + .Select(x => x.Value) + .ToList(); + + var parameters = gherkinStepRegistration.Method.GetParameters(); + + var expectedParameterCount = step.Argument == null ? stepTextArguments.Count : stepTextArguments.Count + 1; + if (expectedParameterCount != parameters.Length) + { + throw new Exception($"Method {gherkinStepRegistration.Type.FullName}.{gherkinStepRegistration.Method.Name} have invalid parameters count"); + } - gherkinStepRegistration.Method.Invoke(targetType, arguments.ToArray()); - } - catch (Exception exception) - { - testResult.Outcome = TestOutcome.Failed; - testResult.ErrorMessage = exception.InnerException?.Message ?? exception.Message; - testResult.ErrorStackTrace = exception.InnerException?.StackTrace ?? exception.StackTrace; - frameworkHandle.RecordResult(testResult); - frameworkHandle.RecordEnd(testResult.TestCase, testResult.Outcome); - return; - } + var arguments = stepTextArguments.Select((value, index) => Convert.ChangeType(value, parameters[index].ParameterType)); + if (step.Argument is DataTable dataTable) + { + arguments = arguments.Concat(new[] { dataTable }); } - testResult.Outcome = TestOutcome.Passed; - frameworkHandle.RecordResult(testResult); - frameworkHandle.RecordEnd(testResult.TestCase, testResult.Outcome); + try + { + return arguments.ToArray(); + } + catch (Exception exception) + { + throw new Exception($"Unable to parse arguments for step: {step.Keyword.Trim()} {step.Text}", exception); + } } } \ No newline at end of file diff --git a/src/NGherkin.TestAdapter/StepExecutionContext.cs b/src/NGherkin.TestAdapter/StepExecutionContext.cs new file mode 100644 index 0000000..5c6e65b --- /dev/null +++ b/src/NGherkin.TestAdapter/StepExecutionContext.cs @@ -0,0 +1,10 @@ +using System.Reflection; + +namespace NGherkin.TestAdapter; + +internal sealed class StepExecutionContext(object target, MethodInfo methodInfo, object[] arguments) +{ + public object Target { get; } = target; + public MethodInfo MethodInfo { get; } = methodInfo; + public object[] Arguments { get; } = arguments; +}