Skip to content
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

Implicit boolean conversion in parameterized tests should be strict and accept only true or false #3177

Closed
s-rwe opened this issue Feb 27, 2023 · 3 comments · Fixed by #3178

Comments

@s-rwe
Copy link
Contributor

s-rwe commented Feb 27, 2023

We encountered a glitch in parameterized tests, where tests can be unintentionally running with false values for an argument, for example if there is a typo in the arguments of a @CsvSource. If such tests still happen to be successful, it's likely that no one will notice the mistake.

The implicit conversion is just using Boolean::valueOf under the hood AFAICS (StringToBooleanAndCharPrimitiveConverter), which is very lenient in terms of "bad" input, which all just gets mapped to false.

Sample snippet to show problematic cases (which would have clearly been intended to be "true" values):

  @ParameterizedTest
  @CsvSource(value = {
      "ẗrue",
      "tru"
  })
  public void testAllArgsTrue(Boolean value) {
    Assertions.assertEquals(value, true);
  }

Since this can hide problems and prevent tests from running through as intended, it should be reasonable to make the implicit conversion just fail on "invalid" input, to show mistakes and force developers to correct them accordingly (i.e. ensure that only values true + false are acceptable, with case-insensitive matching).

This is also an example for which customizing the implicit argument converters could be helpful (#853 which was closed, I think that possibility doesn't exist). But IMO it should be fine to just adjust the default conversion in general, to avoid such accidental mistakes in general.

@sbrannen
Copy link
Member

Hi @s-rwe,

Congratulations on raising your first issue for JUnit 5! 👍

I agree that we should explicitly check for true or false, ignoring case.

So I'm slating this for 5.10.

Are you interested in submitting a PR to address this?

@s-rwe
Copy link
Contributor Author

s-rwe commented Feb 27, 2023

Sure, thanks - created PR #3178 accordingly

@sbrannen sbrannen changed the title Implicit boolean conversion should be more strict and fail on typos Implicit boolean conversion in parameterized tests should be strict and accept only true or false Feb 27, 2023
@sbrannen sbrannen linked a pull request Feb 27, 2023 that will close this issue
6 tasks
marcphilipp added a commit that referenced this issue Apr 28, 2023
Cause use of anything other than `true` or `false` (case-insensitive)
fail in conversion, to make potential typos explicitly visible (instead
of implicitly just treating all else as `false`).

Includes a few tests for failures on invalid values, for conversions
handled by `StringToBooleanAndCharPrimitiveConverter`.

Resolves #3177.

Co-authored-by: Marc Philipp <mail@marcphilipp.de>
sbrannen added a commit that referenced this issue Sep 22, 2023
This commit updates the tests introduced in the last commit in order to
align with the expected behavior in 5.10.x.

See #3177
See #3472
sbrannen added a commit that referenced this issue Sep 22, 2023
This commit updates the tests introduced in the last commit in order to
align with the expected behavior in 5.10.x.

See #3177
See #3472
@Treverix
Copy link

Treverix commented Oct 6, 2023

This was a BAD idea and breaks many existing tests on our side.

The general rule is that it looks for a constructor or factory method which takes a string and calls that to create the object. For Boolean, this means that the string (case insensitive) 'true' creates a Boolean.TRUE and everything else Boolean.FALSE.

We actually made use of this for creating karnaugh tables in CSV sources, which have three values: true, false and don't care. The don't care is added as a '?' or 'X' symbol and creates a Boolean.FALSE (which is ok, could also be Boolean.TRUE - because; we don't care).

Real (but simple) Example:

    @ParameterizedTest
    @CsvSource({
            // isReadOnly, hasWriteAccess, isEditable
            "  false,      true,           true",
            "  false,      false,          false",
            "  true,       ?,              false",
    })
    void isEditableTest(final boolean isReadOnly, final boolean hasWriteAccess, final boolean isEditable) {
    //...
    }

If something is read-only, we don't care if the user has write access or not - it is not editable.

Sure, we could visit all tests now and replace '?' or 'X' by 'false' but that immediately changes the semantics of the test.

IMHO, this should never have been accepted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants