【问题标题】:Parameterized unit test suites参数化单元测试套件
【发布时间】:2014-01-20 09:43:47
【问题描述】:

我正在尝试设置一些参数化测试套件,不幸的是到目前为止没有任何运气。 我有两组参数,我想以所有可能的组合运行多个测试用例(它们在不同的类中)。我试图用 JUnit4 来做,但我无法正确设置它。这将是我的基本想法:

  1. TestSuite1.class设置一组参数,然后启动TestSuite2.class
  2. TestSuite2.class 设置第二组参数,然后开始使用这两个参数的实际测试。

同时似乎不可能同时在RunWith 注释中设置Suite.classParameterized.class(根据谷歌,Parameterized 扩展Suite,我通常得到“无法运行方法找到”消息,如果我使用。)

我的代码基本上是这样的:

TestSuite1.class:

@RunWith(Parameterized.class)
@Parameterized.SuiteClasses({TestSuite2.class})
//I have tried with @RunWith(Suite.class) and
//@Suite.SuiteClasses({TestSuite2.class}) annotations also - all combinations
public class TestSuite1{

  public TestSuite1(int number) {
    Params.first = number;
  } 

  @Parameters
  public static Collection<Object[]> parameters(){
    Object[][] data = new Object[][] { { 1 }, { 2 }, { 3 }, { 4 } };
    return Arrays.asList(data);
  }
}

TestSuite2.class 看起来与TestSuite1.class 相同,只是我在套件中添加了TestCase1.class 而不是TestSuite2,并且它在Params 中设置了另一个变量。

TestCase1.class:

public class TestCase1 {    
  @Test
  public void test1(){
    System.out.println("first: "+Params.first+" second: "+Params.second);
    Assert.assertTrue(true);
  }
}

我对所有想法持开放态度——例如使用 TestNG。我也尝试过(虽然今天是我第一次看到它),但我注意到套件与 JUnit 中的套件有些不同。我不想在测试之前设置 XML 文件,我想以编程方式解决所有设置。

我正在尝试通过任何框架实现的目标吗?


更新:使用 TestNG 我有以下代码:

开始类:

public class Start {

public static void main(String[] args){
    TestListenerAdapter tla = new TestListenerAdapter();
    TestNG testng = new TestNG();
    testng.setTestClasses(new Class[] { FirstTest.class, SecondTest.class });
    testng.addListener(tla);
    testng.run();
}
}

Params.class:

public class Params {
@DataProvider(name = "param")
public static Object[][] createData() {
    Object[][] data = new Object[][] { { 1 }, { 2}, { 3}, { 4} };
    return data;
  }
}

FirstTest.class:

public class FirstTest {

@Test(dataProvider = "param", dataProviderClass = Params.class)
public static void printIt(int number){
    System.out.println("FirstTest: "+number);
}

}

SecondTest.classFirstTest.class 相同。如果我运行它,它将运行FirstTest 4 次,然后运行SecondTest 4 次。我想使用第一组参数运行一次FirstTest,并运行一次SecondTest。然后我想运行一次FirstTestSecondTest,用第二组参数等等。

我尝试设置 setPreserveOrder(true),并尝试了所有 setParallel 选项。然而,在这种方式下,结果是随机顺序的。

(这将是一些硒测试。我知道测试不应该相互依赖,但它仍然是我想要的方式)

【问题讨论】:

    标签: java junit testng test-suite


    【解决方案1】:

    虽然 Parameterized 扩展了 Suite,但它的行为完全不同 - 不尊重 Liskov substitution principle。这是因为通常构造函数Suite(Class&lt;?&gt;, RunnerBuilder) 处理@SuiteClasses 注释。但是Parameterized(Class&lt;?&gt;)@Parameters 的处理替换了这种行为。

    如果您想结合 SuiteParameterized 的行为,您必须在 JUnit 4 之外查看。例如你可以实现你自己的Runner,就像another post here中提到的亚当霍克斯一样。

    我自己也做了同样的事情,并拼凑了一个库,为您提供ParameterizedSuite Runner:https://github.com/PeterWippermann/parameterized-suite

    参数化测试套件如下所示:

    @RunWith(ParameterizedSuite.class)
    @SuiteClasses({OneTest.class, TwoTest.class})
    public class MyParameterizedTestSuite {
        @Parameters(name = "Parameters are {0} and {1}")
        public static Object[] params() {
            return new Object[][] {{'A',1}, {'B',2}, {'C',3}};
        }
    

    【讨论】:

      【解决方案2】:

      据我所知,基本上,您要做的是使用一组参数运行测试。这在 JUnit 中是可能的,这就是为什么用 @Parameters 注释的方法返回一个数组集合(通常是一组集合)。

      看这个例子:

      import static org.junit.runners.Parameterized.Parameters;
      
      @RunWith(Parameterized.class)
      public class TestCase1 {
      
          public TestCase1(int first, int second) {
              Params.first = first;
              Params.second = second;
          }
      
          @Parameters
          public static Collection<Object[]> parameters(){
              Object[][] data = new Object[][] { { 1, 11 }, { 2, 22 }, { 3, 33 }, { 4, 44 } };
              return Arrays.asList(data);
          }
      
          @Test
          public void test1(){
              System.out.println("first: "+Params.first+" second: "+Params.second);
              Assert.assertTrue(true);
          }
      }
      

      编辑: 如果您想在多个测试之间共享参数,您可以在测试用例中使用抽象。

      public class AbstractParametrizedTest {
      
          public AbstractParametrizedTest(int first, int second) {
              Params.first = first;
              Params.second = second;
          }
      
          @Parameterized.Parameters
          public static Collection<Object[]> parameters(){
              Object[][] data = new Object[][] { { 1, 11 }, { 2, 22 }, { 3, 33 }, { 4, 44 } };
              return Arrays.asList(data);
          }
      }
      
      
      @RunWith(Parameterized.class)
      public class TestCase1 extends AbstractParametrizedTest {
      
          public TestCase1(int first, int second) {
              super(first, second);
          }
      
          ...
      }
      

      但我认为最好的方法是使用 TestNGs 数据提供程序。看一下第 5.6.2 节的例子和静态数据提供者的使用 http://testng.org/doc/documentation-main.html

      【讨论】:

      • 我的主要问题是关于套件的。它适用于一个测试类(尽管在这种方式下我需要创建参数的所有组合,但这当然不是问题)-但我想使用套件来实现这一点。我在多个类中有许多测试,我想将它们组织成套件。而且我想用这些参数运行这些套件,而不是每次我开始一个新的测试类时都设置参数。(最好从头到尾制作一个套件50次,而不是做第一个测试类50次,然后是第二个测试类 50 次,依此类推)
      • 我已经用我所拥有的一些 TestNG 代码更新了我的描述。它比 JUnit 更容易工作,但仍不完全是所需的行为。还有其他建议吗?
      【解决方案3】:

      为了实现按顺序执行所有具有相同参数的测试用例的目标,您需要一个不同的Runner,因为该行为在该类中进行。你很幸运,因为这在 JUnit Toolbox ProjectParallelParameterized 类中可用!

      【讨论】:

      • 您能解释一下这应该如何工作吗?我看不出ParallelParameterized 的行为与org.junit.runners.Parameterized 有何不同。如果我将线程数设置为 1 也没关系,不是吗?最重要的是:该注释不适用于所要求的测试套件。
      【解决方案4】:

      这里有一些其他建议似乎更灵活:@RunWith(Enclosed.class)

      简而言之: 而不是@Suite.SuiteClasses(...),只需使用@RunWith(Enclosed.class) 并扩展您的测试类

      @RunWith(Enclosed.class)
      public class FastTest {
      
          public static class Test1FirstAppInit extends AppInitTest {    }
      
          public static class Test2Download extends DownloadTest{    }
      
          public static class Test3OtherTest extends OtherTest {    }
      }
      

      现在参数化:

      @RunWith(Enclosed.class)
      public class FastTest {
      
          private static Iterable<? extends Object> mAllLocale = Arrays.asList(Locale.ENGLISH, Locale.GERMAN);
          private static Iterable<? extends Object> mSingleLocale = Arrays.asList(Locale.ENGLISH);
      
          /*
          Run test class for all Locale
           */
          @RunWith(Parameterized.class)
          public static class Test1FirstAppInit extends AppInitTest {
              @Parameterized.Parameter
              public Locale mLocale;
      
              @Parameterized.Parameters
              public static Iterable<? extends Object> data() {
                  return mAllLocale;
              }
      
              @Override
              public Locale getLocale() {
                  return mLocale;
              }
      
              @Override
              public void test001ResetAll {
                  assumeTrue(false); // skip test completedly
              }
      
      
              @Override
              public void test002ClearAppData() {
                  // replace existing test
                  if (getLocale() != Locale.ENGLISH) {
                      /*
                      should run only on first Locale
                      skip test on following Parameter runs
                       */
                      assumeTrue(false); // skip test
                  }
                  else {
                      super.test000ClearAppData();
                  }
              }
          }
      
          /*
          Run test class only for one Locale
           */
          @RunWith(Parameterized.class)
          public static class Test2Download extends DownloadTest{
              @Parameterized.Parameter
              public Locale mLocale;
      
              @Parameterized.Parameters
              public static  Iterable<? extends Object> data(){
                  return mSingleLocale;
              }
      
              @Override
              public Locale getLocale() {
                  return mLocale;
              }
      
              @Override
              public void test900Delete() {
                  assumeTrue(false); // skip test
              }
          }
      
          /*
          Test not Parameterized
           */
          public static class Test3OtherTest extends OtherTest {    }
      }
      

      参数化测试的测试类如下所示:

      @RunWith(AndroidJUnit4.class)
      @LargeTest
      @FixMethodOrder(MethodSorters.NAME_ASCENDING)
      public class DownloadTest {
      
          public Locale getLocale() {
              // will be overwritten in @RunWith(Enclosed.class)
              // but we are still able to run test class separatedly
              return Locale.ENGLISH;
          }
      
          @Test
          public void test900Delete() {
              ....
          }
          ....
      }
      

      完全匹配我正在搜索的内容。我可以创建不同的测试场景(完整测试、快速测试……)。只需创建不同的 @RunWith(Enclosed.class) 类并扩展您想要包含的测试。

      唯一的一点似乎是 Enclosed.class 不关心排序顺序(如果对您很重要)。 我通过替换封闭解决了它:

      public class SortedEnclosed extends Suite {
      
          public SortedEnclosed(Class<?> klass, RunnerBuilder builder) throws Throwable {
              super(builder, klass, filterAbstractClasses(klass.getClasses()));
          }
      
          protected static Class<?>[] filterAbstractClasses(final Class<?>[] classes) {
              final List<Class<?>> filteredList= new ArrayList<Class<?>>(classes.length);
      
              for (final Class<?> clazz : classes) {
                  if (!Modifier.isAbstract(clazz.getModifiers())) {
                      filteredList.add(clazz);
                  }
              }
              // this is new (there may be better way with own "@FixClassOrder"...):
              Collections.sort(filteredList, new Comparator<Class<?>>() {
                  @Override
                  public int compare(Class<?> o1, Class<?> o2) {
                      return o1.getSimpleName().compareTo(o2.getSimpleName());
                  }
              });
              //  
              return filteredList.toArray(new Class<?>[filteredList.size()]);
          }
      }
      

      然后使用@RunWith(SortedEnclosed.class)

      【讨论】:

        猜你喜欢
        • 2012-05-14
        • 2016-12-04
        • 2023-03-21
        • 1970-01-01
        • 1970-01-01
        • 2016-05-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多