JUnit5类级别的参数化测试

54

是否可以使用JUnit5的参数化新功能来运行测试类以接收测试参数,而不是在方法级别上进行?

在JUnit 4中,可以使用诸如@RunWith(Parameterized::class)等运行器和继承来将参数数组传递给子类,但我不确定是否可以使用新的JUnit 5 API实现类似的功能。


6
似乎您正在寻找 https://github.com/junit-team/junit5/issues/878。 - mkobit
@mkobit 没错。有没有其他的替代方案? - IS1_SO
可能有一些解决方法,如果您能提供关于您想要实现的内容的示例代码,那将非常有帮助。 - mkobit
类似的热门问题:https://dev59.com/XlYN5IYBdhLWcg3wzKzf - Vadzim
4个回答

21

简短回答
使用JUnit 5无法按照JUnit 4的风格对类创建进行参数化。

幸运的是,将测试逻辑和测试输入数据(参数)分离的意图可以以不同的方式实现。


JUnit 5有自己的方法来制作参数化测试,当然,它与JUnit 4不同。新方法不允许在类级别使用参数化fixtures(即通过其每个测试方法)。

因此,每个参数化测试方法都应明确注释为链接到参数

JUnit 5提供了许多参数源类型,可以在文档指南中找到。

在您的情况下,从Junit 4迁移的最简单方式是使用org.junit.jupiter.params.provider.*@MethodSource@ArgumentsSource而不是@Parameters

JUnit 4:

@RunWith(Parameterized.class)
public class MyTestWithJunit4 {
    @Parameters
    public static Collection<Object[]> data() {
      return Arrays.asList(new Object[][] {     
               { 0, 0, 0 },
               { 1, 2, 3 }, 
               { 5, 3, 8 } 
      });
    }

    int first;
    int second;
    int sum;

    public MyTestWithJunit4(int first, int second, int sum) {
      this.first = first;
      this.second = second;
      this.sum = sum;
    }

    @Test
    public void test() {
      assertEquals(sum, first + second));
    }
}

JUnit 5(使用@MethodSource

class MyTestWithJunit5 {

  @DisplayName("Test with @MethodSource")
  @ParameterizedTest(name = "{index}: ({0} + {1}) => {2})")
  @MethodSource("localParameters")
  void test(int first, int second, int sum) {
    assertEquals(sum, first + second);
  }

  static Stream<Arguments> localParameters() {
    return Stream.of(
        Arguments.of(0, 0, 0),
        Arguments.of(1, 2, 3),
        Arguments.of(5, 3, 8)
    );
  }
}

JUnit 5(使用@ArgumentsSource:

class MyTestWithJunit5 {
  @DisplayName("Test with @ArgumentsSource")
  @ParameterizedTest(name = "{index}: ({0} + {1}) => {2})")
  @ArgumentsSource(Params.class)
  void test(int first, int second, int sum) {
    assertEquals(sum, first + second);
  }

  static class Params implements ArgumentsProvider {
    @Override
    public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
      return Stream.of(
          Arguments.of(0, 0, 0),
          Arguments.of(1, 2, 3),
          Arguments.of(5, 3, 8)
      );
    }
  }
}

考虑到@MethodSource中的方法和@ArgumentsSource中的类可以在任何地方描述,不仅限于测试方法所在的同一类。此外,@MethodSource允许提供多个源方法,因为它的value是一个String[]
一些注释和比较:
在JUnit 4中,我们只能有一个单一的工厂方法提供参数,并且测试应该围绕这些参数构建。相反,JUnit 5在绑定参数方面提供了更多的抽象和灵活性,并将测试逻辑与其次要参数解耦。这允许独立于参数源构建测试,并在需要时轻松更改它们。
依赖需求:
参数化测试特性未包含在核心junit-jupiter-engine中,而是位于单独的依赖项junit-jupiter-params中。

13
Junit 5似乎存在严重不足。我们现在有了“嵌套”功能,可以使用简短而明确的测试方法测试整个流程,但是没有办法使用不同的参数初始化这些流程。 - john16384
8
这是一个非常棒的总结,它展现了可以做些什么,但这并不足够强调一个悲哀的事实(也是回答问题的真正答案):在 Junit 5 中没有办法对类的创建进行参数化,而在 Junit 4 中则可以。 - shabunc

7

创建一个元注解来指定参数化,并将其应用于测试方法:

public class MyTest {
    @ParameterizedTest(name = "{0}")
    @MethodSource("allImplementations")
    @Retention(RetentionPolicy.RUNTIME)
    private @interface TestAllImplementations {
    }

    static Stream<Arguments> allImplementations() {
        return Stream.of(
             Arguments.of("Serial", new SerialWidget()),
             Arguments.of("Concurrent", new ConcurrentWidget())
        );
    }

    @TestAllImplementations
    void myFirstTest(String name, Widget implementation) {
        /* ... */
    }

    @TestAllImplementations
    void mySecondTest(String name, Widget implementation) {
        /* ... */
    }
}

1
我能找到的最好解决方案是将旧测试抽象化。 然后创建一个新的类,其中包含每个变体的嵌套测试。
public class NewParametricTest {
  @Nested
  public class NewParametricTestVariation1Test
    extends AbstractOriginalToBeParameticizedTest {
    public NewParametricTestVariation1Test() {
      super("Some parameter", 1);
    }
  }

  // add others here
}

太棒了!这真是救了我的一天!谢谢你!:) - undefined
@domin 随意点赞吧 :) - undefined

0
@ParameterizedTest()
@MethodSource("dataProvider")
void testWithDataProvider(String str, List<JSONObject> list) {
    System.out.println();

}

static Stream<Arguments> dataProvider() {
    String json = """
            {
                "test":1 
            }
                    """;
    return Stream.of(
            arguments("abc", Arrays.asList(new JSONObject(json))),
            arguments("lemon", Arrays.asList(new JSONObject(json)))
    );

我用下面的方法绕过了这个复杂性。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接