10

I implemented a runner class A.class inherited from BlockJUnit4ClassRunner so that I can annotate tests with @RunWith(A.class). At the same time, sb. else annotate the tests with RunWith(Parameterized.class). It is obvious we cannot use two @RunWith at the same time.

How to solve this problem? or how to merge these two @RunWith?

user389955
  • 9,605
  • 14
  • 56
  • 98

1 Answers1

13

I believe this does what you want:

package so.junit.runner;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.model.InitializationError;
import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParameters;
import org.junit.runners.parameterized.ParametersRunnerFactory;
import org.junit.runners.parameterized.TestWithParameters;

import java.util.Arrays;

@RunWith(Parameterized.class)
@Parameterized.UseParametersRunnerFactory(CustomParameterizedTest.RunnerFactory.class)
public class CustomParameterizedTest {

  @Parameterized.Parameters
  public static Iterable<Integer> data() {
    return Arrays.asList(new Integer[]{1, 2, 3});
  }

  private int i;

  public CustomParameterizedTest(int i) {
    this.i = i;
  }

  @Test
  public void test() {
    System.out.println(i);
  }

  public static class RunnerFactory implements ParametersRunnerFactory {
    @Override
    public org.junit.runner.Runner createRunnerForTestWithParameters(TestWithParameters test) throws InitializationError {
      return new A(test);
    }
  }

  public static class A extends BlockJUnit4ClassRunnerWithParameters {
    private final Object[] parameters;

    public A(TestWithParameters test) throws InitializationError {
      super(test);
      parameters = test.getParameters().toArray(new Object[test.getParameters().size()]);
    }

    @Override
    public Object createTest() throws Exception {
      return getTestClass().getOnlyConstructor().newInstance(parameters);
    }
  }
}

Based on the Javadocs in the JUnit Parameterized class, this is how they expect you to create a custom test runner that supports parameterization.

UPDATE

Updated to name the custom runner A

seanf
  • 6,504
  • 3
  • 42
  • 52
Alex
  • 2,435
  • 17
  • 18
  • Thanks for your answer. But in your code I could not figure out where is my A.class? – user389955 Jan 05 '15 at 02:56
  • `public static class Runner extends BlockJUnit4ClassRunnerWithParameters` is the same as your A.class, it can be your custom runner implementation (and supports parameterization) – Alex Jan 05 '15 at 03:10
  • I corrected the example. The class Runner has to extend you A runner. @Alex Hopefully this is ok for you. I didn't want to create a new Answer. – Stefan Birkner Jan 05 '15 at 09:49
  • 1
    @Stefan, this modified version will not work because the custom runner must extend `BlockJUnit4ClassRunnerWithParameters` to support parameterization. I believe my original example was exactly what the original question asked for, i just named the custom runner `Runner` instead of `A` – Alex Jan 05 '15 at 13:13
  • 2
    It doesn't have to extend `BlockJUnit4ClassRunnerWithParameters`. This feature is designed for reusing existing runners without modifying them. Hence you can simply extend your current runner `A` by an mechanism that injects the parameters. – Stefan Birkner Jan 05 '15 at 14:20
  • Thanks Alex and Stefan. I have trouble to run the code because my system cannot import org.junit.runners.parameterized (shows cannot find the symbol). But I believe the code should be correct. So I accept the answer. – user389955 Jan 09 '15 at 17:59
  • @user389955, according to the JUnit Javadocs at http://junit.org/javadoc/latest/org/junit/runners/Parameterized.html, this class was available starting at `4.0`, what version of JUnit are you using? – Alex Jan 09 '15 at 18:48
  • @Alex: junit = 4. it does not complain import org.junit.runners.Parameterized; it complained: import org.junit.runners.parameterized.XXX – user389955 Jan 09 '15 at 20:29
  • 1
    @user389955, Ah, it looks like they added `ParametersRunnerFactory` in `4.12`, so it seems this customizable Parameterized stuff is pretty new. Hopefully you are able to update your JUnit to the latest version then – Alex Jan 09 '15 at 20:34
  • This doesn't appear to be working with `@MockitoJUnitRunner`. The constructor then has to take a parameter `Class> klass`, so I have no idea how to set the parameters. I'm interested in Stefan Birkner's comment about "simply extending the current runner". Could you possibly explain about this? – mike rodent Nov 23 '16 at 21:46
  • `MockitoJUnitRunner` also doesn't appear to have the methods `createTest` or `getTestClass`. I'll trying looking at the source code but if anyone's cracked this it'd be great to hear from you! – mike rodent Nov 23 '16 at 21:57
  • @mike You're right that it's much more difficult for Mockito's runner, because it doesn't directly inherit from BlockJUnit4ClassRunner, but instead is a wrapper around a Runner surrogate in an internal package. In general this would probably require modifying the original runner or rewrapping a deliberately-opaque org.mockito.internal.runners implementation, but for this particular case the point is moot because the MockitoJUnitRunner is [all but deprecated in favor of an equivalent Rule](https://github.com/mockito/mockito/issues/391) anyway. – Jeff Bowman Nov 24 '16 at 17:27