nishantvas
nishantvas

Reputation: 304

Is it possible to disable a Parameterized test basis arguments in Junit5

I have a scenario where I'm trying to disable parameterized tests but I don't want to disable all of them. Consider the below mock scenario

@CsvSource({
      "1,1",
      "2,2",
      "3,not3"
  })
  @ExtendWith(DisableParameterized.class)
  @ParameterizedTest( name = "Test for {0} is equal to {1}")
  void equality(String expected, String actual) {
    assertEquals(expected, actual, "Not as expected");
  }

Is it possible to write a DisableParameterized implements ExecutionCondition which can disable one of the Parameterized tests basis some meta data.

The only proper info I can make out about ParameterizedTests in the

public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext extensionContext)

is the displayName Is there some other way to generically provide some sort of meta data to disable the Parameterized tests ?

if I mark them as @Disabled, it disables all of the ParameterizedTests to the point that it doesn't even invoke the individual tests and stops at the method level.

Edit : As mentioned by Sam below, it's not possible to do it using standard API till the issues mentioned are fixed/implemented and release in a newer version. I was however able to write a code using reflection to achieve what I wanted, so I've added an answer below

Upvotes: 5

Views: 3021

Answers (2)

nishantvas
nishantvas

Reputation: 304

Currently, till the actual API is implemented under the issues Sam has mentioned, the only way to extract arguments is as below. i.e. using reflection Which is a dirty way for most people, but that's the only way

Then after extracting the arguments, we can manipulate them however we want.

@Override
  public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
    return disableRequiredOnParameterizedTests(context);
  }

private ConditionEvaluationResult disableRequiredOnParameterizedTests(ExtensionContext context) {
    // See if my custom annotation is present on the test
    var onParameters = findRepeatableAnnotation(context, DisableOnParameter.class);
    // Or if the test is a Parameterized test after all
    // We have access to arguments when the test will be at method level, i.e. MethodExtensionContext
    if (!MethodExtensionContext.class.isAssignableFrom(context.getClass())
        || findAnnotations(context, ParameterizedTest.class).isEmpty()
        || onParameters.isEmpty()) {
      return enabled("Not set to disable");
    }
    List<Object> arguments = getArguments(context);
    boolean enable = true;
    // Do what you want with arguments
    .
    .
    .
    return enable ? enabled("Enabling") ? disabled("Disabling");
  }

/**
   * A blanket exception safe function to extract arguments from Extension context using
   * reflection since we don't have direct access to the API
   * https://github.com/junit-team/junit5/issues/1139
   *
   * @param context needs to be of {@link MethodExtensionContext} type since only at that stage
   *     arguments can be extracted
   * @return List of argument objects, empty if any error occurs
   */
  private static List<Object> getArguments(ExtensionContext context) {
    try {
      if(MethodExtensionContext.class.isAssignableFrom(context.getClass())) {
        return emptyList();
      }
      Method method = findMethod(context.getClass(), "getTestDescriptor").orElseThrow();
      TestMethodTestDescriptor descriptor =
          (TestMethodTestDescriptor) invokeMethod(method, context);

      // Get the TestTemplateInvocationContext
      Field templateField = descriptor.getClass().getDeclaredField("invocationContext");
      templateField.setAccessible(true);
      TestTemplateInvocationContext template =
          (TestTemplateInvocationContext) templateField.get(descriptor);

      // Get the params finally
      Field argumentsField = template.getClass().getDeclaredField("arguments");
      argumentsField.setAccessible(true);
      return asList((Object[]) argumentsField.get(template));
    } catch (Throwable ignored) {
      return emptyList();
    }
  }

Upvotes: 1

Sam Brannen
Sam Brannen

Reputation: 31247

No, as of JUnit Jupiter 5.6 (pending GA release at the time of this writing), there is no way to access the arguments used to invoked a parameterized test.

For the time being, the only way to infer that type of information is indeed via the display name.

The following open JUnit 5 issues are related to this topic.

Upvotes: 1

Related Questions