Skip to content

Commit

Permalink
Parse scenario steps before execution
Browse files Browse the repository at this point in the history
  • Loading branch information
Romfos committed Dec 16, 2023
1 parent 59dcc06 commit 6d73972
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 53 deletions.
122 changes: 69 additions & 53 deletions src/NGherkin.TestAdapter/NGherkinTestExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,73 +62,89 @@ private void RunTest(TestCase testCase, IFrameworkHandle frameworkHandle)
using var scope = testCaseExecutionContext.Services.CreateScope();
var gherkinStepRegistrations = scope.ServiceProvider.GetServices<GherkinStepRegistration>();

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<GherkinStepRegistration> 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<Group>()
.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<Group>()
.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);
}
}
}
10 changes: 10 additions & 0 deletions src/NGherkin.TestAdapter/StepExecutionContext.cs
Original file line number Diff line number Diff line change
@@ -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;
}

0 comments on commit 6d73972

Please sign in to comment.