I am using Cucumber 7.18 with Java 21. My test engineer is agreeable enough to write tests scenarios. I return, I wish to make error messages as readable as possible.
I have troubles showing up the error message when it happens in DataTable
transformations.
For example, say that the test contains the table below. There is an error in column CODE
at row number 3, because it should always be integers:
Given My system produces the following entries:
| NAME | ORC | CODE | EPT | EFL | TIME |
| AFR89YB | A | 4455 | DEP | 100 | -00:10 |
| AFR89YC | B | 4455 | DEP | 100 | -00:10 |
| AFR89YD | C | Illegal value | DEP | 100 | -00:10 |
| ... | ... | ... | ... | ... | ... |
To decode the table I register a @DataTableType
:
@DataTableType
public MySystemEntry mySystemEntry(Map<String, String> entry) {
return MySystemEntry.builder()
.entry(entry)
.build();
}
And, in the builder, I got plenty of validation rules, like this one:
public class MySystemEntry {
...
public static Builder builder() {
return new Builder();
}
...
public static Builder {
...
public Builder entry(Map<String, String> entry) {
...
...
String value = entry.get("CODE");
if (notANumber(value)) {
throw new IllegalArgumentException("Value " + value + " is illegal for Column 'CODE'.");
}
...
}
...
}
}
When values are correct, everything is good. When values are incorrect, I can see my special exception fire up by setting up a break point in the throw new IllegalArgumentException
. Higher up in the call stack it becomes a InvocationTargetException
, still keeping the cause. Higher up it becomes a CucumberInvocationTargetException
, but the cause is placed in a special invocationTargetException
property. Finally, it becomes a CucumberDataTableException
, and the special property invocationTargetException
is ignored (and therefore lost).
As expected, the test fails. My problem is that the error log is quite unhelpful as it doesn't show the detailed error message specifically crafted my me:
Step failed
io.cucumber.datatable.CucumberDataTableException: 'java.util.List<com.my-application.MySystemEntry>' could not transform
| NAME | ORC | CODE | EPT | EFL | TIME |
| AFR89YB | A | 4455 | DEP | 100 | -00:10 |
| AFR89YC | B | 4455 | DEP | 100 | -00:10 |
| AFR89YD | C | Illegal value | DEP | 100 | -00:10 |
| ... | ... | ... | ... | ... | ... |
at io.cucumber.datatable.DataTableType.transform(DataTableType.java:158)
...
...
at ✽.Given My system produces the following entries: (file:///D:/dev/java/my-application/src/test/resources/features/my-system-works.feature:26)
Caused by: io.cucumber.core.backend.CucumberInvocationTargetException
at io.cucumber.java.Invoker.doInvoke(Invoker.java:73)
...
...
For me, this is a bit of an annoyance - if I can't see the problem, I set a break point and pinpoint exactly what's amiss. But my test engineer is completely lost with the error message. He has to check every value, in every column and row, until he finds the mismatch.
What is the correct way of reporting custom exceptions happening when decoding DataTable
in Cucumber?
EDIT
Following suggestion of @m-p-korstanje, I forked cucumber-java-skeleton
and made a working example here:
maven/src/test/java/io/cucumber/skeleton/StepDefinitions.java
: There you'll see there a method annotated with @DataTableType
that just throws an exception.maven
foldermaven clean test
You'll get a nice stack trace if you let Cucumber do the conversion for you by declaring the type you want in the step definition method.
@When("I mix the following ingredients")
public void i_wait_hour(List<Ingredient> ingredients) {
// use ingredients here
}
Scenario: a few cukes # io/cucumber/skeleton/belly.feature:3
Given I have 42 cukes in my belly # io.cucumber.skeleton.StepDefinitions.I_have_cukes_in_my_belly(int)
When I mix the following ingredients # io.cucumber.skeleton.StepDefinitions.i_wait_hour(java.util.List<io.cucumber.skeleton.StepDefinitions$Ingredient>)
| NAME | QUANTITY | UNITS |
| Flour | 1 | KG |
| Water | 0.65 | L |
| Salt | 0.08 | KG |
java.lang.IllegalArgumentException: This message is never shown during test execution
at io.cucumber.skeleton.StepDefinitions.mySystemEntry(StepDefinitions.java:29)
at ✽.I mix the following ingredients(classpath:io/cucumber/skeleton/belly.feature:6)