5

我有一个 JUnit 3.x TestCase,我希望能够对其进行参数化。我想参数化整个TestCase(包括夹具)。但是,该TestSuite.addTestSuite()方法不允许传递一个TestCase对象,只传递一个类:

   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()`

有任何想法吗?我是否以错误的方式解决问题?

4

5 回答 5

3

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

于 2008-10-13T12:21:06.410 回答
3

我不会为您要测试的多个/不同后端创建参数化测试用例,而是考虑让我的测试用例抽象化。API 的每个新实现都需要提供一个实现的 TestCase 类。

如果您目前有一个看起来像的测试方法

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

只需向返回要使用的特定 API 对象的 TestCase 添加一个抽象方法。

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

然后,您要测试的新实现的 TestCase 只需实现您的 AbstractTestCase 并提供 API 类的具体实现:

public class ImplementationXTestCase extends AbstractTestCase{

    public API getAPIToTest() {
        return new ImplementationX();
    }
}

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

于 2008-10-13T14:00:54.790 回答
3

好的,这是 JUnit 4 如何运行参数化测试的快速模型,但在 JUnit 3.8.2 中完成。

基本上,我正在继承并严重劫持 TestSuite 类,以根据 testMethods 和参数的叉积填充测试列表。

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

尽管如此,这似乎在TestRunnerJUnit 以及 Eclipse 附带的文本和 AWT 中运行良好。

这是 ParameterizedTestSuite,以及使用它的参数化测试的(愚蠢的)示例。

(最后一点:我在写这个时考虑到了 Java 5,如果需要的话适应 1.4应该很简单)

参数化TestSuite.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这里的变量只是为了表明所有测试实例都应该是不同的,并且它们之间没有共享状态。

于 2009-02-04T18:42:36.163 回答
1

一些细节并不完美,例如 IDE 中的测试名称在参数集之间是相同的(JUnit 4.x 附加 [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 + "]";
 }
于 2011-02-21T07:26:03.680 回答
1

对于 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,因此不能真正回答您的问题,但是许多仍然使用 JUnit 3 的项目都这样做,因为 Android 的测试框架需要它,所以我希望其他读者会觉得这很有帮助。

于 2014-10-21T17:42:08.070 回答