diff --git a/CHANGELOG.md b/CHANGELOG.md index c609ad5a3b..9415831cd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed +- [Core] Enhanced stack trace to include step location for better debugging in case of datatable conversion errors ([#2908](https://github.com/cucumber/cucumber-jvm/pull/2908) Thomas Deblock) + ## [7.18.1] - 2024-07-18 ### Changed - [Core] Include parameterized scenario name in JUnit and TestNG XML report diff --git a/cucumber-core/src/main/java/io/cucumber/core/runner/PickleStepDefinitionMatch.java b/cucumber-core/src/main/java/io/cucumber/core/runner/PickleStepDefinitionMatch.java index 2b9fc03769..84ad6b0580 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/runner/PickleStepDefinitionMatch.java +++ b/cucumber-core/src/main/java/io/cucumber/core/runner/PickleStepDefinitionMatch.java @@ -18,7 +18,6 @@ import java.util.List; import java.util.stream.Collectors; -import static io.cucumber.core.runner.StackManipulation.removeFrameworkFrames; import static io.cucumber.core.runner.StackManipulation.removeFrameworkFramesAndAppendStepLocation; class PickleStepDefinitionMatch extends Match implements StepDefinitionMatch { @@ -51,13 +50,13 @@ public void runStep(TestCaseState state) throws Throwable { } catch (CucumberExpressionException | CucumberDataTableException | CucumberDocStringException e) { CucumberInvocationTargetException targetException; if ((targetException = causedByCucumberInvocationTargetException(e)) != null) { - throw removeFrameworkFrames(targetException); + throw removeFrameworkFramesAndAppendStepLocation(targetException, getStepLocation()); } throw couldNotConvertArguments(e); } catch (CucumberBackendException e) { throw couldNotInvokeArgumentConversion(e); } catch (CucumberInvocationTargetException e) { - throw removeFrameworkFrames(e); + throw removeFrameworkFramesAndAppendStepLocation(e, getStepLocation()); } try { stepDefinition.execute(result.toArray(new Object[0])); diff --git a/cucumber-core/src/test/java/io/cucumber/core/runner/StepDefinitionMatchTest.java b/cucumber-core/src/test/java/io/cucumber/core/runner/StepDefinitionMatchTest.java index a954529504..5fe6701fb7 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/runner/StepDefinitionMatchTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/runner/StepDefinitionMatchTest.java @@ -257,11 +257,15 @@ void rethrows_target_invocation_exceptions_from_parameter_type() { StepExpression expression = stepExpressionFactory.createExpression(stepDefinition); CoreStepDefinition coreStepDefinition = new CoreStepDefinition(id, stepDefinition, expression); List arguments = coreStepDefinition.matchedArguments(step); - StepDefinitionMatch stepDefinitionMatch = new PickleStepDefinitionMatch(arguments, stepDefinition, null, step); + StepDefinitionMatch stepDefinitionMatch = new PickleStepDefinitionMatch(arguments, stepDefinition, + URI.create("test.feature"), step); Executable testMethod = () -> stepDefinitionMatch.runStep(null); RuntimeException actualThrown = assertThrows(RuntimeException.class, testMethod); assertThat(actualThrown, sameInstance(userException)); + assertThat( + lastStackElement(actualThrown.getStackTrace()), + is(new StackTraceElement("✽", "I have some cukes in my belly", "test.feature", 3))); } @Test @@ -312,11 +316,15 @@ void rethrows_target_invocation_exceptions_from_data_table() { StepExpression expression = stepExpressionFactory.createExpression(stepDefinition); CoreStepDefinition coreStepDefinition = new CoreStepDefinition(id, stepDefinition, expression); List arguments = coreStepDefinition.matchedArguments(step); - StepDefinitionMatch stepDefinitionMatch = new PickleStepDefinitionMatch(arguments, stepDefinition, null, step); + StepDefinitionMatch stepDefinitionMatch = new PickleStepDefinitionMatch(arguments, stepDefinition, + URI.create("test.feature"), step); Executable testMethod = () -> stepDefinitionMatch.runStep(null); RuntimeException actualThrown = assertThrows(RuntimeException.class, testMethod); assertThat(actualThrown, sameInstance(userException)); + assertThat( + lastStackElement(actualThrown.getStackTrace()), + is(new StackTraceElement("✽", "I have some cukes in my belly", "test.feature", 3))); } @Test @@ -367,11 +375,15 @@ void rethrows_target_invocation_exception_for_docstring() { StepExpression expression = stepExpressionFactory.createExpression(stepDefinition); CoreStepDefinition coreStepDefinition = new CoreStepDefinition(id, stepDefinition, expression); List arguments = coreStepDefinition.matchedArguments(step); - StepDefinitionMatch stepDefinitionMatch = new PickleStepDefinitionMatch(arguments, stepDefinition, null, step); + StepDefinitionMatch stepDefinitionMatch = new PickleStepDefinitionMatch(arguments, stepDefinition, + URI.create("test.feature"), step); Executable testMethod = () -> stepDefinitionMatch.runStep(null); RuntimeException actualThrown = assertThrows(RuntimeException.class, testMethod); assertThat(actualThrown, sameInstance(userException)); + assertThat( + lastStackElement(actualThrown.getStackTrace()), + is(new StackTraceElement("✽", "I have some cukes in my belly", "test.feature", 3))); } @Test @@ -465,6 +477,10 @@ void throws_could_not_invoke_step_when_execution_failed_with_null_arguments() { "The converted arguments types were (null)"))); } + private StackTraceElement lastStackElement(StackTraceElement[] stackTrace) { + return stackTrace[stackTrace.length - 1]; + } + private static final class ItemQuantity { private final String s;