【问题标题】:ParameterizedTest with a name in Eclipse TestrunnerEclipse Testrunner 中带有名称的 ParameterizedTest
【发布时间】:2008-12-22 10:16:19
【问题描述】:

当您使用 Eclipse TestRunner 运行 JUnit 4 ParameterizedTest 时,图形表示相当愚蠢:对于每个测试,您都有一个名为 [0][1] 等的节点。 是否可以给测试[0][1] 等提供明确的名称?为测试实现toString 方法似乎没有帮助。

(这是JUnit test with dynamic number of tests的后续问题。)

【问题讨论】:

    标签: java eclipse unit-testing junit parameterized-unit-test


    【解决方案1】:

    我认为 jUnit 4 中没有任何内置功能可以做到这一点。

    我已经实施了一个解决方案。我已经在现有的基础上构建了自己的 Parameterized 类:

    public class MyParameterized extends TestClassRunner {
        @Retention(RetentionPolicy.RUNTIME)
        @Target(ElementType.METHOD)
        public static @interface Parameters {
        }
    
        @Retention(RetentionPolicy.RUNTIME)
        @Target(ElementType.METHOD)
        public static @interface Name {
        }
    
        public static Collection<Object[]> eachOne(Object... params) {
            List<Object[]> results = new ArrayList<Object[]>();
            for (Object param : params)
                results.add(new Object[] { param });
            return results;
        }
    
        // TODO: single-class this extension
    
        private static class TestClassRunnerForParameters extends TestClassMethodsRunner {
            private final Object[] fParameters;
    
            private final Class<?> fTestClass;
    
            private Object instance;
    
            private final int fParameterSetNumber;
    
            private final Constructor<?> fConstructor;
    
            private TestClassRunnerForParameters(Class<?> klass, Object[] parameters, int i) throws Exception {
                super(klass);
                fTestClass = klass;
                fParameters = parameters;
                fParameterSetNumber = i;
                fConstructor = getOnlyConstructor();
                instance = fConstructor.newInstance(fParameters);
            }
    
            @Override
            protected Object createTest() throws Exception {
                return instance;
            }
    
            @Override
            protected String getName() {
                String name = null;
                try {
                    Method m = getNameMethod();
                    if (m != null)
                        name = (String) m.invoke(instance);
                } catch (Exception e) {
                }
                return String.format("[%s]", (name == null ? fParameterSetNumber : name));
            }
    
            @Override
            protected String testName(final Method method) {
                String name = null;
                try {
                    Method m = getNameMethod();
                    if (m != null)
                        name = (String) m.invoke(instance);
                } catch (Exception e) {
                }
                return String.format("%s[%s]", method.getName(), (name == null ? fParameterSetNumber : name));
            }
    
            private Constructor<?> getOnlyConstructor() {
                Constructor<?>[] constructors = getTestClass().getConstructors();
                assertEquals(1, constructors.length);
                return constructors[0];
            }
    
            private Method getNameMethod() throws Exception {
                for (Method each : fTestClass.getMethods()) {
                    if (Modifier.isPublic((each.getModifiers()))) {
                        Annotation[] annotations = each.getAnnotations();
                        for (Annotation annotation : annotations) {
                            if (annotation.annotationType() == Name.class) {
                                if (each.getReturnType().equals(String.class))
                                    return each;
                                else
                                    throw new Exception("Name annotated method doesn't return an object of type String.");
                            }
                        }
                    }
                }
                return null;
            }
        }
    
        // TODO: I think this now eagerly reads parameters, which was never the
        // point.
    
        public static class RunAllParameterMethods extends CompositeRunner {
            private final Class<?> fKlass;
    
            public RunAllParameterMethods(Class<?> klass) throws Exception {
                super(klass.getName());
                fKlass = klass;
                int i = 0;
                for (final Object each : getParametersList()) {
                    if (each instanceof Object[])
                        super.add(new TestClassRunnerForParameters(klass, (Object[]) each, i++));
                    else
                        throw new Exception(String.format("%s.%s() must return a Collection of arrays.", fKlass.getName(), getParametersMethod().getName()));
                }
            }
    
            private Collection<?> getParametersList() throws IllegalAccessException, InvocationTargetException, Exception {
                return (Collection<?>) getParametersMethod().invoke(null);
            }
    
            private Method getParametersMethod() throws Exception {
                for (Method each : fKlass.getMethods()) {
                    if (Modifier.isStatic(each.getModifiers())) {
                        Annotation[] annotations = each.getAnnotations();
                        for (Annotation annotation : annotations) {
                            if (annotation.annotationType() == Parameters.class)
                                return each;
                        }
                    }
                }
                throw new Exception("No public static parameters method on class " + getName());
            }
        }
    
        public MyParameterized(final Class<?> klass) throws Exception {
            super(klass, new RunAllParameterMethods(klass));
        }
    
        @Override
        protected void validate(MethodValidator methodValidator) {
            methodValidator.validateStaticMethods();
            methodValidator.validateInstanceMethods();
        }
    
    }
    

    用法如下:

    @RunWith(MyParameterized.class)
    public class ParameterizedTest {
        private File file;
        public ParameterizedTest(File file) {
            this.file = file;
        }
    
        @Test
        public void test1() throws Exception {}
    
        @Test
        public void test2() throws Exception {}
    
        @Name
        public String getName() {
            return "coolFile:" + file.getName();
        }
    
        @Parameters
        public static Collection<Object[]> data() {
            // load the files as you want
            Object[] fileArg1 = new Object[] { new File("path1") };
            Object[] fileArg2 = new Object[] { new File("path2") };
    
            Collection<Object[]> data = new ArrayList<Object[]>();
            data.add(fileArg1);
            data.add(fileArg2);
            return data;
        }
    }
    

    这意味着我更早地实例化了测试类。我希望这不会导致任何错误......我想我应该测试测试:)

    【讨论】:

    【解决方案2】:

    JUnit4 现在将allows specifying a name attribute 添加到参数化注解中,这样您就可以从参数的索引和 toString 方法中指定命名模式。例如:

    @Parameters(name = "{index}: fib({0})={1}")
    public static Iterable<Object[]> data() {
        return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 },
                { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } });
    }
    

    【讨论】:

      【解决方案3】:

      一种无代码但不是那么舒服的解决方案是传递足够的上下文信息来识别断言消息中的测试。您仍然会看到 testXY[0] 失败,但详细消息会告诉您是哪一个。

      assertEquals("Not the expected decision for the senator " + this.currentSenatorName + " and the law " + this.votedLaw, 
      expectedVote, actualVote);
      

      【讨论】:

        【解决方案4】:

        如果您使用JUnitParams library(就像我使用described here),参数化测试将使用它们的字符串化参数作为它们自己的默认测试名称。

        此外,you can see in their samples,JUnitParams 还允许您使用 @TestCaseName 来自定义测试名称:

        @Test
        @Parameters({ "1,1", "2,2", "3,6" })
        @TestCaseName("factorial({0}) = {1}")
        public void custom_names_for_test_case(int argument, int result) { }
        

        @Test
        @Parameters({ "value1, value2", "value3, value4" })
        @TestCaseName("[{index}] {method}: {params}")
        public void predefined_macro_for_test_case_name(String param1, String param2) { }
        

        【讨论】:

          【解决方案5】:

          没有任何迹象表明此功能已经实现或将要实现。我会要求这个功能,因为它很好。

          【讨论】:

            猜你喜欢
            • 2020-01-13
            • 2017-11-04
            • 2010-12-13
            • 1970-01-01
            • 2012-06-13
            • 1970-01-01
            • 2023-04-02
            • 1970-01-01
            • 2023-04-02
            相关资源
            最近更新 更多