You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
While working on Jakarta Data , we noticed that some test display names match the same pattern. Then we figured out a way to simplify those display names by creating a custom display name generator as you can see on this Issue and PR.
We wanted to implement only on our side, we faced some issues while trying to make this generator available for all Jakarta Data modules. Those issues leaded to many discussions about this feature, its added value, its scope....
Thinking about this for a long time, we asked ourselves why only Jakarta Data ? Why not all Jakarta projects ? Even more, why not make it available for everybody by adding this custom generator to JUnit directly ? Given that, this pattern is often used, it will help more developers.
That's why I am opening this issue to share this generator with the world 😊 !
To get a better idea, I share with you the code of this custom generator.
/** * <p>A class extending {@linkplain DisplayNameGenerator.Standard }.</p> * * <p>This extension handles method names with CamelCase, underscore and numbers.</p> * * <p>The aim is to simplify unit test display names. Instead of using this method annotation {@linkplain org.junit.jupiter.api.DisplayName }, * we can just use this class annotation {@linkplain org.junit.jupiter.api.DisplayNameGeneration } and use that method annotation if needed. * </p> * * <p>This generator follows 3 rules:</p> * * <ul> * <li>Each uppercase letter is turned into its lowercase value prepended by space.</li> * <li>Each underscore is turned into space. Words bounded by underscores or just starting with underscore are not transformed. Usually these words words represent classes, variables....</li> * <li>Each number is prepended by space.</li> * </ul> * <p> * Usage example: * * <pre> * * {@code * * @DisplayNameGeneration(ReplaceCamelCaseAndUnderscoreAndNumber.class) * class ExampleTest { * @Test * //Equivalent of @DisplayName("Should return error when maxResults is negative") * void shouldReturnErrorWhen_maxResults_IsNegative() { * ... * } * @Test * //Equivalent of @DisplayName("Should create limit with range") * void shouldCreateLimitWithRange() { * ... * } * * @Test * //Equivalent of @DisplayName("Should return 5 errors") * void shouldReturn5Errors() { * ... * } * * @ParameterizedTest * @ValueSource(strings = {"job", "player"}) * //Equivalent of @DisplayName("Should return the value of maxResults (String)") * void shouldReturnTheValueOf_maxResults(String input) { * ... * } * * @Test * //Equivalent of @DisplayName("Should return the number of errors as numberOfErrors inferior or equal to 15") * void shouldReturnTheNumberOfErrorsAs_numberOfErrors_InferiorOrEqualTo15() { * ... * } * } * * } * </pre> */publicclassReplaceCamelCaseAndUnderscoreAndNumberextendsDisplayNameGenerator.Standard {
publicstaticfinalDisplayNameGeneratorINSTANCE = newReplaceCamelCaseAndUnderscoreAndNumber();
@OverridepublicStringgenerateDisplayNameForMethod(Class<?> testClass, MethodtestMethod) {
if (hasParameters(testMethod)) {
returnreplaceCamelCaseAndUnderscoreAndNumber(testMethod.getName()) + " " + DisplayNameGenerator.parameterTypesAsString(testMethod);
}
returnreplaceCamelCaseAndUnderscoreAndNumber(testMethod.getName());
}
privateStringreplaceCamelCaseAndUnderscoreAndNumber(Stringinput) {
StringBuilderresult = newStringBuilder();
/* * Each method name starts with "should" then the displayed name starts with "Should" * */result.append(Character.toUpperCase(input.charAt(0)));
/* * There are 2 groups of method name: with and without underscore * */if (input.contains("_")) {
booleaninsideUnderscores = false;
for (inti = 1; i < input.length(); i++) {
charcurrentChar = input.charAt(i);
if (currentChar == '_') {
result.append(' ');
/* * If the current char is an underscore and insideUnderscores is true, * it means there is an opening underscore and this one is the closing one * then we set insideUnderscores to false. * *//* * If the current char is an underscore and insideUnderscores is false, * it means there is not an opening underscore and this one is the opening one * then we set insideUnderscores to true. * */insideUnderscores = !insideUnderscores;
} else {
/* * If the character is inside underscores, we append the character as it is. * */if (insideUnderscores) {
result.append(currentChar);
} else {
//CamelCase handling for method name containing "_"if (Character.isUpperCase(currentChar)) {
//We already replace "_" with " ". If the previous character is "_", we will not add extra spaceif (!(input.charAt(i - 1) == '_')) {
result.append(' ');
}
result.append(Character.toLowerCase(currentChar));
} else {
result.append(currentChar);
}
}
}
}
} else {
//CamelCase handling for method name not containing "_"for (inti = 1; i < input.length(); i++) {
if (Character.isUpperCase(input.charAt(i))) {
result.append(' ');
result.append(Character.toLowerCase(input.charAt(i)));
} else {
result.append(input.charAt(i));
}
}
}
/*Add space before all numbers * Nothing is done after number because each number must be followed by an uppercase letter. Thus, there will be space between these two. * In case of a lowercase letter following number, this will be considered as the user's choice. Thus, there will be no space between these two. * */returnresult.toString().replaceAll("(\\d+)", " $1");
}
privatebooleanhasParameters(Methodmethod) {
returnmethod.getParameterCount() > 0;
}
}
The text was updated successfully, but these errors were encountered:
sbrannen
changed the title
Add new custom DisplayNameGenerator ReplaceCamelCaseAndUnderscoreAndNumber
Introduce DisplayNameGenerator to support CamelCase, underscores, and numbers
Nov 21, 2023
Hi @sbrannen , it's my pleasure to contribute JUnit after using it for years now. I am looking forward to here from the team. Yes, they are similar but this one goes further.
While working on Jakarta Data , we noticed that some test display names match the same pattern. Then we figured out a way to simplify those display names by creating a custom display name generator as you can see on this Issue and PR.
We wanted to implement only on our side, we faced some issues while trying to make this generator available for all Jakarta Data modules. Those issues leaded to many discussions about this feature, its added value, its scope....
Thinking about this for a long time, we asked ourselves why only Jakarta Data ? Why not all Jakarta projects ? Even more, why not make it available for everybody by adding this custom generator to JUnit directly ? Given that, this pattern is often used, it will help more developers.
That's why I am opening this issue to share this generator with the world 😊 !
To get a better idea, I share with you the code of this custom generator.
The text was updated successfully, but these errors were encountered: