如何在 JUnit 测试中创建一个可选的 Parameterized.Parameter?

How to make an optional Parameterized.Parameter in JUnit test?

我必须使用 JUnit 测试,该测试具有来自带有注释 @Parameterized.Parameters(name = "{0}") 的集合的自动装配参数,该集合中已经有很多测试。我想加一个参数,但是作为大多数程序员,我是一个很懒惰的人,所以我想引入一个可选参数,可以吗?

@Parameterized.Parameter()
importantParameter;

@Parameterized.Parameter(1)
evenMoreImportantParameter;

@Parameterized.Parameter(2)
businessHandshakeBusinessYes;

@Parameterized.Parameter(3)
meBeingLazyOptionalParameter;

@Parameterized.Parameters(name = "{0}")
public static Collection<Object[]> data() {
    Collection<Object[]> params = new ArrayList<>();
    params.add(new Object[]{ "wow", "very", "impressive"});
    params.add(new Object[]{ "wow", "much", "beautiful"});

    // Where I want to be lazy
    params.add(new Object[]{ "this", "is", "sooooo", "better"});
        
    return params;
}

在前面显示的情况下,只有最后一个测试会成功,前 2 个测试将生成以下错误消息

java.lang.Exception: Wrong number of parameters and @Parameter fields. @Parameter fields counted: 4, available parameters: 3.

快速查看 Parameterized.classBlockJUnit4ClassRunnerWithParameters.class 的源代码表明无法指定可选参数。

就个人而言,我会保持所有参数的大小相同,但您始终可以创建单元运行程序,复制 Parameterized.class 并使用 null.[=17 填充数组至最大大小=]

这是一个 Parameterized 执行填充的运行器示例(JUnit 中原始运行器的副本,但使用了私有方法 allParameters 改编):


public class Parameterized extends Suite {

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public static @interface Parameters {

        String name() default "{index}";

        // The max number of parameters
        int size();
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    @Target(ElementType.TYPE)
    public @interface UseParametersRunnerFactory {

        Class<? extends ParametersRunnerFactory> value() default BlockJUnit4ClassRunnerWithParametersFactory.class;
    }

    private static final ParametersRunnerFactory DEFAULT_FACTORY = new BlockJUnit4ClassRunnerWithParametersFactory();

    private static final List<Runner> NO_RUNNERS = Collections.<Runner>emptyList();

    private final List<Runner> runners;
    private final int  size;


    public Parameterized(Class<?> klass) throws Throwable {
        super(klass, NO_RUNNERS);
        ParametersRunnerFactory runnerFactory = getParametersRunnerFactory( klass);
        Parameters parameters = getParametersMethod().getAnnotation( Parameters.class);
        size = parameters.size();
        runners = Collections.unmodifiableList(createRunnersForParameters( allParameters(), parameters.name(), runnerFactory));

    }

    private ParametersRunnerFactory getParametersRunnerFactory(Class<?> klass)
            throws InstantiationException, IllegalAccessException {
        UseParametersRunnerFactory annotation = klass
                .getAnnotation(UseParametersRunnerFactory.class);
        if (annotation == null) {
            return DEFAULT_FACTORY;
        } else {
            Class<? extends ParametersRunnerFactory> factoryClass = annotation.value();
            return factoryClass.newInstance();
        }
    }

    @Override
    protected List<Runner> getChildren() {
        return runners;
    }

    private TestWithParameters createTestWithNotNormalizedParameters(String pattern, int index, Object parametersOrSingleParameter) {
        Object[] parameters= (parametersOrSingleParameter instanceof Object[])
                ? (Object[]) parametersOrSingleParameter
                : new Object[] { parametersOrSingleParameter };
        return createTestWithParameters(getTestClass(), pattern, index, parameters);
    }

    private Iterable<Object> allParameters() throws Throwable {
        Object parameters = getParametersMethod().invokeExplosively(null);

        if (parameters instanceof Iterable) {
            return pad((Iterable<Object>) parameters);
        } else if (parameters instanceof Object[]) {
            return pad(Arrays.asList((Object[]) parameters));
        } else {
            throw parametersMethodReturnedWrongType();
        }
    }

    private Iterable<Object> pad(Iterable<Object> parameters) {
        List<Object> padded = new ArrayList<>();
        for ( Object parameter : parameters ) {
            Object[] paddedParams = new Object[size];
            Object[] current = (Object[]) parameter;
            System.arraycopy( current, 0, paddedParams, 0, current.length );
            padded.add( paddedParams );
        }
        return padded;
    }

    private FrameworkMethod getParametersMethod() throws Exception {
        List<FrameworkMethod> methods = getTestClass().getAnnotatedMethods(
                Parameters.class);
        for (FrameworkMethod each : methods) {
            if (each.isStatic() && each.isPublic()) {
                return each;
            }
        }

        throw new Exception("No public static parameters method on class " + getTestClass().getName());
    }

    private List<Runner> createRunnersForParameters(
            Iterable<Object> allParameters, String namePattern,
            ParametersRunnerFactory runnerFactory)
            throws InitializationError,
            Exception {
        try {
            List<TestWithParameters> tests = createTestsForParameters(
                    allParameters, namePattern);
            List<Runner> runners = new ArrayList<Runner>();
            for (TestWithParameters test : tests) {
                runners.add(runnerFactory
                        .createRunnerForTestWithParameters(test));
            }
            return runners;
        } catch (ClassCastException e) {
            throw parametersMethodReturnedWrongType();
        }
    }

    private List<TestWithParameters> createTestsForParameters(
            Iterable<Object> allParameters, String namePattern)
            throws Exception {
        int i = 0;
        List<TestWithParameters> children = new ArrayList<TestWithParameters>();
        for (Object parametersOfSingleTest : allParameters) {
            children.add(createTestWithNotNormalizedParameters(namePattern,
                    i++, parametersOfSingleTest));
        }
        return children;
    }

    private Exception parametersMethodReturnedWrongType() throws Exception {
        String className = getTestClass().getName();
        String methodName = getParametersMethod().getName();
        String message = MessageFormat.format( "{0}.{1}() must return an Iterable of arrays.", className, methodName);
        return new Exception(message);
    }

    private static TestWithParameters createTestWithParameters(
            TestClass testClass, String pattern, int index, Object[] parameters) {
        String finalPattern = pattern.replaceAll("\{index\}", Integer.toString(index));
        String name = MessageFormat.format(finalPattern, parameters);
        return new TestWithParameters("[" + name + "]", testClass, Arrays.asList(parameters));
    }
}