代码之家  ›  专栏  ›  技术社区  ›  JesperE

JUnit3.x中的参数化测试用例类

  •  4
  • JesperE  · 技术社区  · 16 年前

    我有一个JUnit3.x测试用例,我希望它能够参数化。我想参数化整个 TestCase (包括夹具)。然而, TestSuite.addTestSuite() 方法不允许通过 测试用例 对象,只是一个类:

       TestSuite suite = new TestSuite("suite");
       suite.addTestSuite(MyTestCase.class);
    

    我希望能够将参数(字符串)传递给mytestcase实例,该实例是在测试运行时创建的。现在,每个参数值都必须有一个单独的类。

    我试着把它作为一个子类:

       MyTestCase testCase = new MyTestCase() {
           String getOption() {
               return "some value";
           }
       }
    
       suite.addTestSuite(testCase.getClass());
    

    但是,这一断言失败了:

       ... MyTestSuite$1 has no public constructor TestCase(String name) or TestCase()`
    

    有什么想法吗?我是不是以错误的方式来解决这个问题?

    5 回复  |  直到 10 年前
        1
  •  2
  •   Powerlord    16 年前

    如果这是Java 5或更高版本,您可能需要考虑切换到JUnit 4,它支持内置的参数化测试用例。

        2
  •  3
  •   matt b    16 年前

    与其为要测试的多个/不同的后端创建参数化的测试用例,我倒不如将我的测试用例抽象化。API的每个新实现都需要提供一个实现测试用例类。

    如果您当前有一个类似于

    public void testSomething() {
       API myAPI = new BlahAPI();
       assertNotNull(myAPI.something());
    }
    

    只需向测试用例添加一个抽象方法,该方法返回要使用的特定API对象。

    public abstract class AbstractTestCase extends TestCase {
        public abstract API getAPIToTest();
    
        public void testSomething() {
           API myAPI = getAPIToTest();
           assertNotNull(myAPI.something());
        }
    
        public void testSomethingElse() {
           API myAPI = getAPIToTest();
           assertNotNull(myAPI.somethingElse());
        }
    }
    

    然后,您只需测试新实现的测试用例就可以实现抽象测试用例,并提供API类的具体实现:

    public class ImplementationXTestCase extends AbstractTestCase{
    
        public API getAPIToTest() {
            return new ImplementationX();
        }
    }
    

    然后,在抽象类中测试API的所有测试方法都将自动运行。

        3
  •  3
  •   Thomas Dufour    15 年前

    好的,这里有一个JUnit4如何运行参数化测试的快速模型,但是在JUnit3.8.2中完成了。

    基本上,我正在子类化并严重劫持testsuite类,以根据testmethods和参数的交叉乘积填充测试列表。

    不幸的是,我不得不从testsuite本身复制几个helper方法,一些细节并不完美,例如,IDE中的测试名称在参数集之间是相同的(junit 4.x附录 [0] , [1] ,……)

    然而,这似乎在文本和awt中运行良好 TestRunner 那艘和朱尼特一起的船也在日食中。

    这里是参数化测试套件,接下来是一个(愚蠢的)使用它的参数化测试的例子。

    (最后注意:我已经用Java 5编写了它, 应该 如有需要,可根据1.4调整)

    参数化测试套件.java:

    package junit.parameterized;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    import java.util.ArrayList;
    import java.util.Collection;
    
    import junit.framework.Test;
    import junit.framework.TestCase;
    import junit.framework.TestSuite;
    
    public class ParameterizedTestSuite extends TestSuite {
    
        public ParameterizedTestSuite(
                final Class<? extends TestCase> testCaseClass,
                final Collection<Object[]> parameters) {
    
            setName(testCaseClass.getName());
    
            final Constructor<?>[] constructors = testCaseClass.getConstructors();
            if (constructors.length != 1) {
                addTest(warning(testCaseClass.getName()
                        + " must have a single public constructor."));
                return;
            }
    
            final Collection<String> names = getTestMethods(testCaseClass);
    
            final Constructor<?> constructor = constructors[0];
            final Collection<TestCase> testCaseInstances = new ArrayList<TestCase>();
            try {
                for (final Object[] objects : parameters) {
                    for (final String name : names) {
                        TestCase testCase = (TestCase) constructor.newInstance(objects);
                        testCase.setName(name);
                        testCaseInstances.add(testCase);
                    }
                }
            } catch (IllegalArgumentException e) {
                addConstructionException(e);
                return;
            } catch (InstantiationException e) {
                addConstructionException(e);
                return;
            } catch (IllegalAccessException e) {
                addConstructionException(e);
                return;
            } catch (InvocationTargetException e) {
                addConstructionException(e);
                return;
            }
    
    
            for (final TestCase testCase : testCaseInstances) {
                addTest(testCase);
            }       
        }
        private Collection<String> getTestMethods(
                final Class<? extends TestCase> testCaseClass) {
            Class<?> superClass= testCaseClass;
            final Collection<String> names= new ArrayList<String>();
            while (Test.class.isAssignableFrom(superClass)) {
                Method[] methods= superClass.getDeclaredMethods();
                for (int i= 0; i < methods.length; i++) {
                    addTestMethod(methods[i], names, testCaseClass);
                }
                superClass = superClass.getSuperclass();
            }
            return names;
        }
        private void addTestMethod(Method m, Collection<String> names, Class<?> theClass) {
            String name= m.getName();
            if (names.contains(name))
                return;
            if (! isPublicTestMethod(m)) {
                if (isTestMethod(m))
                    addTest(warning("Test method isn't public: "+m.getName()));
                return;
            }
            names.add(name);
        }
    
        private boolean isPublicTestMethod(Method m) {
            return isTestMethod(m) && Modifier.isPublic(m.getModifiers());
         }
    
        private boolean isTestMethod(Method m) {
            String name= m.getName();
            Class<?>[] parameters= m.getParameterTypes();
            Class<?> returnType= m.getReturnType();
            return parameters.length == 0 && name.startsWith("test") && returnType.equals(Void.TYPE);
         }
    
        private void addConstructionException(Exception e) {
            addTest(warning("Instantiation of a testCase failed "
                    + e.getClass().getName() + " " + e.getMessage()));
        }
    
    }
    

    参数化测试.java:

    package junit.parameterized;
    import java.util.Arrays;
    import java.util.Collection;
    
    import junit.framework.Test;
    import junit.framework.TestCase;
    import junit.parameterized.ParameterizedTestSuite;
    
    
    public class ParameterizedTest extends TestCase {
    
        private final int value;
        private int evilState;
    
        public static Collection<Object[]> parameters() {
            return Arrays.asList(
                    new Object[] { 1 },
                    new Object[] { 2 },
                    new Object[] { -2 }
                    );
        }
    
        public ParameterizedTest(final int value) {
            this.value = value;
        }
    
        public void testMathPow() {
            final int square = value * value;
            final int powSquare = (int) Math.pow(value, 2) + evilState;
            assertEquals(square, powSquare);
            evilState++;
        }
    
        public void testIntDiv() {
            final int div = value / value;
            assertEquals(1, div);
        }
    
        public static Test suite() {
            return new ParameterizedTestSuite(ParameterizedTest.class, parameters());
        }
    }
    

    注: evilState 变量只是在这里显示所有的测试实例都应该是不同的,并且它们之间没有共享状态。

        4
  •  1
  •   user626150    13 年前

    一些细节并不完美,比如IDE中的测试名称在参数集之间是相同的(junit 4.x appends[0],[1],…)。

    要解决此问题,只需覆盖getname()并更改测试用例类中的构造函数:

     private String displayName;
    
     public ParameterizedTest(final int value) {
         this.value = value;
         this.displayName = Integer.toString(value);
     }
    
     @Override
     public String getName() {
         return super.getName() + "[" + displayName + "]";
     }
    
        5
  •  1
  •   Daniel Lubarov    10 年前

    对于Android项目,我们编写了一个名为 Burst 用于测试参数化。例如

    public class ParameterizedTest extends TestCase {
      enum Drink { COKE, PEPSI, RC_COLA }
    
      private final Drink drink;
    
      // Nullary constructor required by Android test framework
      public ConstructorTest() {
        this(null);
      }
    
      public ConstructorTest(Drink drink) {
        this.drink = drink;
      }
    
      public void testSomething() {
        assertNotNull(drink);
      }
    }
    

    因为你没有使用Android,所以这个问题并不是真正的答案,但是很多仍然使用JUnit3的项目都是这样做的,因为Android的测试框架需要它,所以我希望其他一些读者会发现这一点有帮助。