-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Remove or relax the new rule for strict String to Boolean conversion #3490
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Have you considered using
In addition, you can already configure what you consider Combining those two concepts gives us the following, which I imagine might suit your needs. @ParameterizedTest
@CsvSource(delimiter = '|', nullValues = {"?", "x", "X"}, textBlock = """
# isReadOnly | hasWriteAccess | isEditable
# -----------+----------------+------------
false | true | true
# -----------+----------------+------------
false | false | false
true | ? | false
# -----------+----------------+------------
""")
void isEditableTest(Boolean isReadOnly, Boolean hasWriteAccess, Boolean isEditable) {
// ...
} |
@sbrannen Is there a difference between using Release notes say
Documentation says now:
and I received reports that some or many tests wouldn't work after an upgrade, but up to this point I only assumed, that it would affect both 'boolean' and 'Boolean' at the same time. If using Even if release notes and documention would be misleading (RN) or event incorrect (docs) |
Or as further alternatives, you could also consider explicit argument conversion for such values, instead of the implicit one - which would also give you full control over which actual value the "?"-values should become mapped to for the tests at runtime (like always That should work via either:
IMO that would also make it much more explicit (towards the actual test-code) that you're really dealing with a 3-value type here. |
The release notes and user guide are perhaps a bit misleading. The strictness for what constitutes However, a I was therefore suggesting that you could use a |
@sbrannen Ah, I see. But that's different semantics. 'null' is absence which is different from 'don't care'. And we couldn't use true, false, null and don't care on the same input table. 'null' is no input, '?' is any input (but never nothing). Also, I want to avoid 'null' as input, as it will lead to NPEs on outboxing. And I want to avoid extra boilerplate in the test, which converts each It would have to map the @ParameterizedTest
@CsvSource(delimiter = '|', nullValues = {"?", "x", "X"}, textBlock = """
# isFoo | isBar | isFooBar | isBaz | expectedResult
# -------+-------+----------+-------+----------------
true | ? | ? | ? | true
false | true | ? | ? | true
false | false | true | ? | true
false | false | false | true | true
# -------+-------+----------+-------+----------------
false | false | false | false | true
# -------+-------+----------+-------+----------------
""")
void isEditableTest(boolean isFoo, Boolean isBar_, Boolean isFooBar_, Boolean isBaz_, boolean expectedResult) {
boolean isBar = isBar_ == null ? DONT_CARE : isBar_; // DONT_CARE is a static const like boolean DONT_CARE=false
boolean isFooBar = isFooBar_ == null ? DONT_CARE : isFooBar_;
boolean isBaz = isBaz_ == null ? DONT_CARE : isBaz_;
// ...
} |
@Treverix, if using |
@s-rwe For special type like record KarnaughBoolean(boolean value) {
public static KarnaughBoolean from(final String value) {
return new KarnaughBoolean(Boolean.parseBoolean(value));
}
} and use it in the test like so @ParameterizedTest
@CsvSource(delimiter = '|', textBlock = """
# isReadOnly | hasWriteAccess | isEditable
# -----------+----------------+------------
false | true | true
# -----------+----------------+------------
false | false | false
true | ? | false
# -----------+----------------+------------
""")
void isEditableTest(final KarnaughBoolean isReadOnly, final KarnaughBoolean hasWriteAccess, final KarnaughBoolean isEditable) {
// ...
assertThat(result).isEqualTo(isEditable.value());
} Basically, I'd have to invest in boilerplate to revert to the pre-5.10 behavior. |
@sbrannen See me last comment. A custom type seems to be the cheapest workaround (which still has to be communicated to all devs), but I still don't understand, why a breaking change was introduced just to make it easier for unexperienced users to spot their typos. At the cost of others, who now have to invest in change test code to be able to update JUnit on their side. JUnit is not a typo checker. It's not it's purpose. |
@Treverix - yes, each of these variants needs some amount of changes, we're just trying to point out alternatives. I don't understand what should be complex for Developers when using the So taking one of the sample signatures above, it could look like this if an
While that does indeed clutter the signatures up a bit, I still think it makes things just more explicit. The nice thing with that approach (compared to the other alternative I mentioned) is that actual test-code would otherwise not need to be changed at all, it's purely the signatures that would need to be adjusted. |
As @s-rwe mentioned above, it's not all that complicated, and you don't need to compute any indexes. For example... package example;
import org.junit.jupiter.params.converter.TypedArgumentConverter;
class KarnaughBooleanConverter extends TypedArgumentConverter<String, Boolean> {
KarnaughBooleanConverter() {
super(String.class, Boolean.class);
}
@Override
protected Boolean convert(String value) {
return Boolean.parseBoolean(value);
}
} package example;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.jupiter.params.converter.ConvertWith;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ConvertWith(KarnaughBooleanConverter.class)
public @interface KarnaughBoolean {
} package example;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
class KarnaughTests {
@ParameterizedTest
@CsvSource(delimiter = '|', textBlock = """
# isFoo | isBar | isFooBar | isBaz | expectedResult
# -------+-------+----------+-------+----------------
true | ? | ? | ? | true
false | true | ? | ? | true
false | false | true | ? | true
false | false | false | true | true
# -------+-------+----------+-------+----------------
false | false | false | false | true
# -------+-------+----------+-------+----------------
""")
void isEditableTest(boolean isFoo, @KarnaughBoolean boolean isBar, @KarnaughBoolean boolean isFooBar,
@KarnaughBoolean boolean isBaz, boolean expectedResult) {
// ...
}
} With this approach, you only need to annotate targeted You could also shorten the name of |
@Treverix, I understand your concerns, and we'll discuss this topic within the team. |
@s-rwe sorry, my bad, I confused it with the So |
Team decision: Keep the strict behavior since alternative solutions exist as described in #3490 (comment) |
#3178 made String to Boolean conversion more strict (added with version 5.10.0).
I strongly suggest to remove that feature or relax on the rule, because now we can't use our karnaugh maps (KM) on CSV sources anymore.
Karnaugh maps allow three values: false, true and don't care (or 0, 1, don't care).
Consider this (simple) example. We test if a method correctly computes, that a form is editable
If 'read-only' is true, then we don't care about user access rights - it's not editable. The implementation is not required to check that. Technically, we get a
false
injected, but: we don't care. Can be false or true. The implementation does not care and neither does the test. It shall work with false and true and it's ok to test with one of both.Karnaugh maps can get a lot more complex, if the method under test has more then just two logical conditions. Those KM in CSV sources greatly improve readability and maintainability of unit tests.
#3178 makes that impossible now, because '?' or 'X' for 'don't care' are no accepted values anymore.
The common rule in JUnit is that the system looks for a constructor or static factory method, that takes a String. Here, it uses either
new Boolean(String)
orBoolean.valueOf(String)
and both returnBoolean.TRUE
only if the String istrue
(case insensitive). #3178 breaks that rule.I understand, that this is helpful to detect typos in parameterized test values. But I still think, that it adds more problems then value.
I suggest, that JUnit get's updated and either
?
andx
(case insensitve) so that we can keep our karnaugh maps in CSV sources.The text was updated successfully, but these errors were encountered: