【问题标题】:Writing Java tests with data providers使用数据提供者编写 Java 测试
【发布时间】:2013-10-05 23:19:43
【问题描述】:

我目前正在做我的第一个 Java 项目,并希望对其进行完全 TDD。我正在使用 JUnit 编写测试。显然 JUnit 不提供对data providers 的支持,这使得用 20 个不同版本的参数测试同一个方法相当烦人。支持数据提供者的最流行/标准的 Java 测试工具是什么?我遇到了TestNG,但不知道它有多受欢迎,或者它与替代品相比如何。

如果有一种方法可以让这种行为成为使用 JUnit 的好方法,那么这也可能有效。

【问题讨论】:

  • JUnit 有参数化测试。
  • 但请注意,用于参数化 JUnit 测试的 Eclipse 集成很糟糕。 a) 无法运行单个参数集,b) 很难从 JUnit 窗口中分辨出哪个参数在测试期间实际失败(您会在列表中看到类似 [0]、[1]、[2] 的内容)。
  • @qqilihq JUnit 4.11 现在可以更好地命名参数化测试,这样您就不会再遇到这种问题了github.com/junit-team/junit/blob/master/doc/ReleaseNotes4.11.md
  • @dkatzel 谢谢,会看看!

标签: java testing junit


【解决方案1】:

我公司的同事为 JUnit 编写了一个免费的 TestNG 风格的 DataProvider,您可以找到 on github (https://github.com/TNG/junit-dataprovider)。

我们在非常大的项目中使用它,它对我们来说效果很好。它比 JUnit 的 Parameterized 有一些优势,因为它可以减少单独类的开销,并且您也可以执行单个测试。

一个例子看起来像这样

@DataProvider
public static Object[][] provideStringAndExpectedLength() {
    return new Object[][] {
        { "Hello World", 11 },
        { "Foo", 3 }
    };
}

@Test
@UseDataProvider( "provideStringAndExpectedLength" )
public void testCalculateLength( String input, int expectedLength ) {
    assertThat( calculateLength( input ) ).isEqualTo( expectedLength );
}

编辑: 从 v1.7 开始,它还支持其他提供数据的方式(字符串、列表),并且可以内联提供程序,因此不一定需要单独的方法。

可以在 github 的手册页上找到完整的工作示例。它还具有更多功能,例如收集实用程序类中的提供程序并从其他类访问它们等。手册页非常详细,我相信您会在那里找到任何问题的答案。

【讨论】:

  • 对于那些尝试运行上述示例的人:不要忘记在测试类中添加 @RunWith(DataProviderRunner.class) 注释!
【解决方案2】:

JUnit 4 具有参数化测试,与 php 数据提供者做同样的事情

@RunWith(Parameterized.class)
public class MyTest{ 
     @Parameters
    public static Collection<Object[]> data() {
           /*create and return a Collection
             of Objects arrays here. 
             Each element in each array is 
             a parameter to your constructor.
            */

    }

    private int a,b,c;


    public MyTest(int a, int b, int c) {
            this.a= a;
            this.b = b;
            this.c = c;
    }

    @Test
    public void test() {
          //do your test with a,b
    }

    @Test
    public void testC(){
        //you can have multiple tests 
        //which all will run

        //...test c
    }
}

【讨论】:

  • 这是相似的,虽然不一样。 PHP 数据提供者将参数传递给您的方法。在您的示例中,参数被传递给测试类的构造函数。如果我有多个测试方法,我想要一个值列表怎么办?为每个创建一个测试类会很尴尬。
  • 您可以有多个测试方法,每个方法都用@Test 注释。将进行所有测试。我稍微更新了我的答案以显示如何
  • 当您有多个测试使用相同的参数列表或来自同一列表的元素时,这很有用。如果没有重叠,它并没有真正的帮助。就我而言,我有一个有效输入列表和一个无效输入列表,它们没有重叠。把它们放在一起会非常奇怪。
  • @JeroenDeDauw 数据提供者主要用于测试单个测试的各种场景,而无需复制所涉及的代码,同时允许每个场景在任何给定场景失败的情况下运行。如果您可以构建数据提供程序,以便多个不同的测试可以使用它们,那么这就是 lagniappe。
【解决方案3】:

根据您对灵活性和可读性的需求,您可以选择Parameterized - junit 的内置选项,由 dkatzel 描述。其他选项是由外部库(如 zohhak)提供的外部 junit 运行器,您可以这样做:

 @TestWith({
        "clerk,      45'000 USD, GOLD",
        "supervisor, 60'000 GBP, PLATINUM"
    })
    public void canAcceptDebit(Employee employee, Money money, ClientType clientType) {
        assertTrue(   employee.canAcceptDebit(money, clientType)   );
    }

或junitParams,功能稍有不同。随便挑最适合自己的吧

【讨论】:

    【解决方案4】:

    您可以使用 JUnit 5 的 ParameterizedTest。这是来自https://www.petrikainulainen.net/programming/testing/junit-5-tutorial-writing-parameterized-tests/ 的示例:

    import org.junit.jupiter.api.DisplayName;
    import org.junit.jupiter.params.ParameterizedTest;
    import org.junit.jupiter.params.provider.Arguments;
     
    import java.util.stream.Stream;
     
    import static org.junit.jupiter.api.Assertions.assertEquals;
     
    @DisplayName("Should pass the method parameters provided by the sumProvider() method")
    class MethodSourceExampleTest {
     
        @DisplayName("Should calculate the correct sum")
        @ParameterizedTest(name = "{index} => a={0}, b={1}, sum={2}")
        @MethodSource("sumProvider")
        void sum(int a, int b, int sum) {
            assertEquals(sum, a + b);
        }
     
        private static Stream<Arguments> sumProvider() {
            return Stream.of(
                    Arguments.of(1, 1, 2),
                    Arguments.of(2, 3, 5)
            );
        }
    }
    

    可以从注释、方法甚至 CSV 文件中加载测试参数。

    【讨论】:

      【解决方案5】:

      这是另一种选择。您不必使用 Google Guava,这只是我的实现。

      这使用与@dkatzel 的答案相同的@Parameters,但@Parameters 注释不是使用参数,而是使用特定的测试方法,因此您可以选择哪些方法使用该组参数。

      import java.util.Collection;
      
      import com.google.common.collect.ImmutableList;
      
      import junitparams.JUnitParamsRunner;
      import junitparams.Parameters;
      import org.junit.Assert;
      import org.junit.Test;
      import org.junit.runner.RunWith;
      
      @RunWith(JUnitParamsRunner.class)
      public class FrobTester {
          @SuppressWarnings("unused")
          private Collection validfrobAndGorpValues() {
              return ImmutableList.of(
                  new Object[] {"frob28953", 28953},
                  new Object[] {"oldfrob-189-255", 1890255}
              );
          }
      
          @Test
          @Parameters(method = "validfrobAndGorpValues")
          public void whenGivenFrobString_thenGorpIsCorrect(
              String frobString,
              int expectedGorpValue
          ) {
              Frob frob = new Frob(frobString);
              Assert.assertEquals(expectedGorpValue, frob.getGorpValue());
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-16
        • 1970-01-01
        相关资源
        最近更新 更多