JUnit4参数化测试 – @Theory 和@DataPoints详解

后端 潘老师 2年前 (2023-10-24) 179 ℃ (0) 扫码查看

在我之前关于这个主题的帖子中,我介绍了如何使用@Parameters注释编写参数化测试用例。 如果我用正确的词,那么这种方法可能很混乱,而且不太容易阅读。 这需要很多不必要的注意力。 好吧,还有另一种方法可以帮助您使用JUnit中的注释,如@Theory 和@DataPoints来编写参数化测试用例。

我将以前面的例子为例,并将其转换为新的方法。 这只是为了说明,因为在这之后我们将能够比较什么被改变了,以及与以前的做法有多大的不同。

1)使用@DataPoints提供输入数据

在这里,只有注释已经从@Parameters更改为@DataPoints。 其余的概念是相同的。

以前,提供输入的方法是:

@Parameters(name = "Run #Square of : {0}^2={1}")
public static Iterable<Object []> data() {
  return Arrays.asList(new Object[][] { { 1, 1 }, { 2, 4 }, { 3, 19 },
      { 4, 16 }, { 5, 25 } });
}

现在,它是:

@DataPoints
public static int[][] integers()
{
  return new int[][]{{1, 1}, {2, 4}, {3, 9}, {4, 16}, {5, 25}, {}};
}

请注意,您可以使用@DataPoint注释单独编写输入。

@DataPoint
public static int[] input6 = new int[]{6, 36};
 
@DataPoint
public static int[] input7 = new int[]{7, 49};

我已经将返回类型从“Iterable<object[]>”更改为“int[][]”,因为这些输入被提供给测试用例的方式有所不同。 在下一部分中,您将看到区别。

2)使用@Theory编写测试用例

基于理论的类在结构上比参数化测试类更简单。 类声明应该用@RunWith(Theories.class)注释,并且必须提供两个实体:

  • 一个生成和返回测试数据的data方法
  • 一个Theory

Data方法必须用@DataPoints注释,每个理论必须用@Theory注释。 与普通的单元测试一样,每个理论应至少包含一个断言。

在先前的做法中,我们这样编写测试用例:

@Test
public void testUserMapping() {
  //这里可以使用断言
  Assert.assertEquals(resultExpected, MathUtils.square(input));
}

其中input和resultExpected被声明为类成员并使用参数化构造函数填充。 正如您在上面看到的那样,testUserMapping()方法没有任何参数。

在新方法中,测试用例被标注为@Theory注释。例如:

@Theory
public void testSquares(final int[] inputs)
{
  Assume.assumeTrue(inputs[0] > 0 && inputs[1] > 0);
  Assert.assertEquals(inputs[1], MathUtils.square(inputs[0]));
}

您现在看到的参数是测试用例的一部分,这是这个概念最精彩的部分。assumeTrue()方法确保参数为正数,而assertEquals()方法则检查我们需要测试的函数逻辑。

要运行上述测试用例,可以用@RunWith注解标记该类。

@RunWith(Theories.class)
public class JunitTestsWithParameters
{
  //测试用例
}

如果您认为某些测试用例在执行操作时可能会抛出异常,可以用@Rule注解和ExpectedException类来处理。下面给出了一个更完整的示例:

@RunWith(Theories.class)
public class JunitTestsWithParameters
{
   @Rule
   public ExpectedException expectedException = ExpectedException.none();
 
   @DataPoints
   public static int[][] integers()
   {
      return new int[][]{{1, 1}, {2, 4}, {3, 9}, {4, 16}, {5, 25}, {}};
   }
 
   @DataPoint
   public static int[] input6 = new int[]{6, 36};
   @DataPoint
   public static int[] input7 = new int[]{7, 49};
 
   @Theory
   public void testSquares(final int[] inputs)
   {
      Assume.assumeTrue(inputs.length == 2);
      Assume.assumeTrue(inputs[0] > 0 && inputs[1] > 0);
      Assert.assertEquals(inputs[1], MathUtils.square(inputs[0]));
   }
 
   @Theory
   public void testBlankArrays(final int[] inputs)
   {
      Assume.assumeTrue(inputs.length == 0);
      expectedException.expect(ArrayIndexOutOfBoundsException.class);
      Assert.assertEquals(inputs[1], MathUtils.square(inputs[0]));
   }
}

运行上述测试用例,结果如下所示:

请注意,将测试数据与测试/理论的实现分离除了简洁之外还可以带来另一个积极的效果:您可能开始考虑与实际要测试的内容无关的测试数据。

但同时,您应该也注意到了,我们没有办法将一个特定的结果与一个特定的数据点相匹配。当您可以用断言的形式表达一个数据点和一个预期结果之间的一般关系,并且这种关系对所有数据都成立时,您应该使用理论。

因此,请仔细考虑理论参数化测试用例之间的选择。它们并不是参数化测试用例的确切替代品,而是与之相辅相成。


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/back/10348.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】