参数化单元测试套件

时间:2014-01-02 20:41:57

标签: java junit testng test-suite

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

  1. TestSuite1.class设置一组参数,然后启动TestSuite2.class
  2. TestSuite2.class设置第二组参数,然后启动将使用这两个参数的实际测试。
  3. 同时,似乎无法同时在Suite.class注释中设置Parameterized.classRunWith(根据谷歌,Parameterized extends {{ 1}},如果我使用,我通常会得到“没有找到可运行的方法”消息。)

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

    TestSuite1.class:

    Suite

    @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中设置了另一个变量}。

    TestCase1.class:

    Params

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

    我想用任何框架实现的目标是什么?


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

    Start.class:

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

    Params.class:

    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();
    }
    }
    

    FirstTest.class:

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

    public class FirstTest { @Test(dataProvider = "param", dataProviderClass = Params.class) public static void printIt(int number){ System.out.println("FirstTest: "+number); } } SecondTest.class相同。如果我运行它,它运行FirstTest.class 4次,然后运行FirstTest 4次。我想一次运行SecondTest,并且FirstTest一次运行第一组参数。然后我想运行SecondTestFirstTest一次,使用第二组参数等。

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

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

4 个答案:

答案 0 :(得分:0)

基本上,据我所知,您要做的是使用一组参数运行测试。这对于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

答案 1 :(得分:0)

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

答案 2 :(得分:0)

虽然Suite延伸Suite(Class<?>, RunnerBuilder),但它的行为完全不同 - 不尊重Liskov substitution principle。这是因为通常构造函数@SuiteClasses处理Parameterized(Class<?>)注释。但是@Parameters会将此行为替换为Suite

的处理

如果要将ParameterizedRunner的行为结合起来,则必须在JUnit 4之外查看。您可以像another post here中提到的Adam Hawkes一样实施自己的ParameterizedSuite

我自己做了同样的事情并拼凑了一个图书馆,为您提供@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}}; } 亚军:https://github.com/PeterWippermann/parameterized-suite

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

{{1}}

答案 3 :(得分:0)

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

简而言之: 而不是@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() {
        ....
    }
    ....
}

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

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(Enclosed.class)类并扩展您想要包含的测试。

只有侧点似乎是Enclosed.class不关心排序顺序(如果对你很重要)。 我通过替换Enclosed来解决它:

@RunWith(SortedEnclosed.class)

然后使用{{1}}