2

使用 JUnit4,我想做的是能够测试一组不同的 java 项目,它们都做同样的事情,但不必写出一个测试用例来测试每个项目,我想知道是否可以编写一个可以在多个类上运行的单个测试?

如果使用 JUnit4 无法做到这一点,是否可以通过其他方式做到这一点?

我知道这是不对的,但这只是为了简要说明我的意思:

@Test
public void test(Class insertClassNameHere, Method nameOfMethod){
    Class insertClassNameHere = new Class();
    assertEquals(insertClassNameHere.nameOfMethod(),1);
}
4

4 回答 4

3

您可以使用 JUnit 的 @Parameterized。

@RunWith(Parameterized.class)
public class BehaviorTest {
    @Parameters
    public static Collection<Object[]> classesAndMethods() {
        List<Object[]> list = new ArrayList<Object[]>();
        list.add(new Object[]{ Foo.class, Foo.class.getMethod("foo") });
        return list;
    }

    private Class clazz;
    private Method method;

    public BehaviorTest(Class clazz, Method method) {
         this.clazz = clazz;
         this.method = method;
    }

    @Test
    public void testBehavior() {
        // Do stuff
    }
}
于 2012-08-02T13:31:06.150 回答
2

怎么样...之类的

for(Class cls: new Class[] { Class1.class, ... } )
    assertEquals(1, nameOfMethod.invoke(cls.newInstance()));
于 2012-08-02T13:29:31.540 回答
1

当然。对此没有技术限制。但这是不可取的。他们将其称为单元测试是有充分理由的。

如果您要对多个类使用相同的测试,那么这通常表明您的应用程序中有重复的代码。


测试类是普通的 java 类,因此您可以使用继承来实现您的想法:一个包含实际测试的基(测试)类和执行一些设置的子(测试)类,iaw,使用实例初始化测试要测试的不同类。您甚至可以使用反射来创建实例和方法对象。

如果您必须为作业准备单元测试,您希望为同一任务提供数十个不同的课程,这可能是一个解决方案。但对于大多数其他情况,我宁愿复制测试方法,然后创建复杂的测试类。

于 2012-08-02T13:29:39.743 回答
0

很抱歉这么晚才跳进去。有类似的需求,我开发了许多演示文稿和培训,展示了重构后给定类随时间的变化。因此,测试是相同的,但类本身会发生变化。每个版本都包含在不同的包中。类名保持不变。这听起来与OP所面临的相似。

我发现参数化解决方案很麻烦。我使用了两种解决方案。它们都是用 Groovy 编写的,但下面的第二个示例应该可以作为纯 Java 解决方案来实现。

这是第一个:

public class ItemTest {
    def defaultConstructedClasses = [
         new com.groovifyingjava.essentialgroovification.Item()
        ,new com.groovifyingjava.rewriteequals.Item()
        ,new com.groovifyingjava.removesemicolons.Item()
        ,new com.groovifyingjava.removeparentheses.Item()
        ,new com.groovifyingjava.removeaccessors.Item()
        ,new com.groovifyingjava.removeconstructors.Item()
        ,new com.groovifyingjava.removeimports.Item()
        ,new com.groovifyingjava.removereturn.Item()
        ,new com.groovifyingjava.clarifyidentityequality.Item()
        ,new com.groovifyingjava.coercetypes.Item()
        ,new com.groovifyingjava.defaultaccessmodifiers.Item()
        ,new com.groovifyingjava.eachiteration.Item()
        ,new com.groovifyingjava.fieldaccessnotation.Item()
        ,new com.groovifyingjava.namedparameters.Item()
        ,new com.groovifyingjava.optionaldatatyping.Item()
        ,new com.groovifyingjava.safelynavigate.Item()
        ,new com.groovifyingjava.simplifylistmapsetcreation.Item()
        ,new com.groovifyingjava.stringinterpolation.Item()
        ,new com.groovifyingjava.useelvisoperator.Item()
        ,new com.groovifyingjava.useequalsoperator.Item()
        ,new com.groovifyingjava.usemathoperators.Item()
        ,new com.groovifyingjava.useprintln.Item()
    ]

    def overloadedConstructorArgs = [itemId: "14-101", manufacturer: "Raleigh", model: "Superbe Roadster", cost: 179.89, quantityOnHand: 10]

    def overloadedConstructedClasses = [
         new com.groovifyingjava.essentialgroovification.Item(overloadedConstructorArgs)
        ,new com.groovifyingjava.rewriteequals.Item(overloadedConstructorArgs)
        ,new com.groovifyingjava.removesemicolons.Item(overloadedConstructorArgs)
        ,new com.groovifyingjava.removeparentheses.Item(overloadedConstructorArgs)
        ,new com.groovifyingjava.removeaccessors.Item(overloadedConstructorArgs)
        ,new com.groovifyingjava.removeconstructors.Item(overloadedConstructorArgs)
        ,new com.groovifyingjava.removeimports.Item(overloadedConstructorArgs)
        ,new com.groovifyingjava.removereturn.Item(overloadedConstructorArgs)
        ,new com.groovifyingjava.clarifyidentityequality.Item(overloadedConstructorArgs)
        ,new com.groovifyingjava.coercetypes.Item(overloadedConstructorArgs)
        ,new com.groovifyingjava.defaultaccessmodifiers.Item(overloadedConstructorArgs)
        ,new com.groovifyingjava.eachiteration.Item(overloadedConstructorArgs)
        ,new com.groovifyingjava.fieldaccessnotation.Item(overloadedConstructorArgs)
        ,new com.groovifyingjava.namedparameters.Item(overloadedConstructorArgs)
        ,new com.groovifyingjava.optionaldatatyping.Item(overloadedConstructorArgs)
        ,new com.groovifyingjava.safelynavigate.Item(overloadedConstructorArgs)
        ,new com.groovifyingjava.simplifylistmapsetcreation.Item(overloadedConstructorArgs)
        ,new com.groovifyingjava.stringinterpolation.Item(overloadedConstructorArgs)
        ,new com.groovifyingjava.useelvisoperator.Item(overloadedConstructorArgs)
        ,new com.groovifyingjava.useequalsoperator.Item(overloadedConstructorArgs)
        ,new com.groovifyingjava.usemathoperators.Item(overloadedConstructorArgs)
        ,new com.groovifyingjava.useprintln.Item(overloadedConstructorArgs)
    ]

    @Test public void canCreateDefaultInstance() {
        for(def item in defaultConstructedClasses) {
            assertNull "Default construction of class ${item.class.name} failed to properly initialize field.", item.itemId
            assertNull "Default construction of class ${item.class.name} failed to properly initialize field.", item.manufacturer
            assertNull "Default construction of class ${item.class.name} failed to properly initialize field.", item.model
            assertNull "Default construction of class ${item.class.name} failed to properly initialize field.", item.cost
            assertEquals "Default construction of class ${item.class.name} failed to properly initialize field.", 0, item.quantityOnHand
            assertEquals "Default construction of class ${item.class.name} failed to properly initialize field.", BigDecimal.ZERO, item.inventoryValue as BigDecimal
        }
    }

    @Test public void canCreateInstancesFromOverloadedConstructor() {
        for(def item in overloadedConstructedClasses) {
            assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.","14-101", item.itemId
            assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.", "Raleigh", item.manufacturer
            assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.", "Superbe Roadster", item.model
            assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.", 179.89, item.cost
            assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.", 10, item.quantityOnHand
            assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.", new BigDecimal("1798.90"), item.inventoryValue as BigDecimal
        }
    }
}

这很好用并且很容易实现。它使用 Groovy 鸭子类型来测试每个类。缺点是 Eclipse 中的 junit 控制台将显示最后一次测试运行。这可能没什么大不了的。此外,它会因测试失败而停止执行。请注意,我在断言消息字符串中包含了类名。这使得识别失败的类变得容易。

第二个版本需要一个抽象测试类,每个包中都有一个测试类,对应于被测类的每个包。这些只是存根,但允许您谨慎地或在套件中运行测试。

public abstract class CommonItemTest {
    def overloadedConstructorArgs = [itemId: "14-101", manufacturer: "Raleigh", model: "Superbe Roadster", cost: 179.89, quantityOnHand: 10]

    @Test public void canCreateDefaultInstance() {
        def item = defaultConstructedClass

        assertNull "Default construction of class ${item.class.name} failed to properly initialize field. Expected itemId to be null.", item.itemId
        assertNull "Default construction of class ${item.class.name} failed to properly initialize field. Expected manufacturer to be null.", item.manufacturer
        assertNull "Default construction of class ${item.class.name} failed to properly initialize field. Expected model to be null.", item.model
        assertNull "Default construction of class ${item.class.name} failed to properly initialize field. Expected cost to be null.", item.cost
        assertEquals "Default construction of class ${item.class.name} failed to properly initialize field. Expected quantityOnHand to be zero.", 0, item.quantityOnHand
        assertEquals "Default construction of class ${item.class.name} failed to properly initialize field. Expected inventoryValue to be zero.", BigDecimal.ZERO, item.inventoryValue as BigDecimal
    }

    @Test public void canCreateInstancesFromOverloadedConstructor() {
        def item = overloadedConstructedClass

        assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.","14-101", item.itemId
        assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.", "Raleigh", item.manufacturer
        assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.", "Superbe Roadster", item.model
        assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.", 179.89, item.cost
        assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.", 10, item.quantityOnHand
        assertEquals "Instantiation of class ${item.class.name} using overloaded constructor failed to properly initialize field.", new BigDecimal("1798.90"), item.inventoryValue as BigDecimal
    }

    abstract Object getDefaultConstructedClass();
    abstract Object getOverloadedConstructedClass();
}

和实施...

public class ItemTest extends CommonItemTest {
    public Object getDefaultConstructedClass() {
        return new Item()
    }
    public Object getOverloadedConstructedClass() {
        return new Item(overloadedConstructorArgs)
    }
}
于 2013-06-07T16:27:42.213 回答