Junit自定义参数化测试

JUnitParameterizedRunner.java


import org.junit.Test;
import org.junit.runner.Description;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
import org.junit.runners.model.TestClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;

/**
 * JUnitParameterizedRunner
 * Describe: The class is a runner of JUnit, It supports parametric testing.
 *
 * Example:
 * @RunWith(JUnitParameterizedRunner.class)
 * public class Test{
 *     @ParametersData(name = "testData")
 *     public static Object[][] data() {
 *          return new Object[][]{
 *              {1, 2, 3},
 *              {4, 5, 9}
 *          }
 *     }
 *
 *     @Test
 *     @Parameters(name = "testData") //@Parameters(name = "testData", dataClass=Test.class)
 *     public void test(int a, int b, int expected) {
 *         Assert.assertEquals(expected, add(a, b));
 *     }
 *
 *     public int add(int a, int b) {
 *         return a + b;
 *     }
 * }
 */
public class JUnitParameterizedRunner extends BlockJUnit4ClassRunner {
    private static final Logger logger = LoggerFactory.getLogger(UnitTestCaseBase.class);
    private final Map<String, Object[][]> parametersMap = new HashMap<>();
    private final Map<Method, Integer> getParamsIndexMap = new HashMap<>();

    public JUnitParameterizedRunner(Class<?> klass) throws InitializationError {
        super(klass);
    }

    @Override
    protected Statement methodInvoker(FrameworkMethod method, Object test) {
        Object[] fParameters = null;
        if (method.getMethod().getParameterCount() > 0) {
            fParameters = getParameters(method);
        }
        return new ParameterInvokeMethod(method, test, fParameters);
    }

    @Override
    protected void validateTestMethods(List<Throwable> errors) {
        validatePublicVoidMethods(Test.class, false, errors);
    }

    @Override
    protected List<FrameworkMethod> getChildren() {
        List<FrameworkMethod> list = super.getChildren();
        List<FrameworkMethod> resultList = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            FrameworkMethod method = list.get(i);
            if (method.getMethod().getParameterCount() > 0) {
                Object[][] params = getParametersList(method);
                if (params != null) {
                    for (int j = 0; j < params.length; j++) {
                        resultList.add(method);
                    }
                }
            } else {
                resultList.add(method);
            }
        }
        return resultList;
    }

    @Override
    protected void runChild(FrameworkMethod method, RunNotifier notifier) {
        Method m = method.getMethod();
        String paramTypes = Arrays.toString(m.getParameterTypes());
        String methodDesc = String.format("%s#%s(%s)",
                m.getDeclaringClass().getTypeName(), m.getName(), paramTypes.substring(1, paramTypes.length()-1));
        RunListener runListener = new RunListener() {
            @Override
            public void testFailure(Failure failure) throws Exception {
                logger.info(String.format("**** Case Failure: %s\n", methodDesc));
            }

            @Override
            public void testIgnored(Description description) throws Exception {
                logger.info(String.format("---- Case Ignored: %s", methodDesc));
            }
        };
        notifier.addFirstListener(runListener);
        logger.info(String.format(">>>> Case Started %s", methodDesc));
        super.runChild(method, notifier);
        logger.info(String.format("<<<< Case Finished: %s\n", methodDesc));
        notifier.removeListener(runListener);

    }

    protected void validatePublicVoidMethods(Class<? extends Annotation> annotation,
                                             boolean isStatic, List<Throwable> errors) {
        List<FrameworkMethod> methods= getTestClass().getAnnotatedMethods(annotation);

        for (FrameworkMethod eachTestMethod : methods)
            eachTestMethod.validatePublicVoid(isStatic, errors);
    }

    private void loadParameters(Class clazz) {
        TestClass testClass;
        if(clazz != Object.class) {
            testClass = new TestClass(clazz);
        } else {
            testClass = getTestClass();
        }
        List<FrameworkMethod> methods = testClass.getAnnotatedMethods(ParametersData.class);
        for (FrameworkMethod method : methods) {
            int modifiers = method.getMethod().getModifiers();
            if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) {
                ParametersData parametersProvider = method.getAnnotation(ParametersData.class);
                try {
                    String key = getParamsKey(clazz, parametersProvider.name());
                    parametersMap.put(key, (Object[][]) method.invokeExplosively(null));
                } catch (Throwable throwable) {
                    throwable.printStackTrace();
                    throw new RunParametersProviderException(String.format("run %s failed, method:%s,exception:%s:%s",
                            parametersProvider.name(), method.getName(), throwable, throwable.getMessage()));
                }
            }
        }
    }

    private String getParamsKey(Class clazz, String name) {
        return String.format("@Parameters(%s, %s)", clazz.getName(), name);
    }

    private Object[][] getParametersList(FrameworkMethod method) {
        Parameters parameters = method.getAnnotation(Parameters.class);
        if (parameters != null) {
            String key = getParamsKey(parameters.dataClass(), parameters.name());
            Object[][] data = parametersMap.get(key);
            if(data == null) {
                loadParameters(parameters.dataClass());
            }
            data = parametersMap.get(key);
            if(data != null && data.length > 0 && data[0].length == method.getMethod().getParameterCount()) {
                return data;
            }
            throw new InvalidParametersConfigException(String.format("Wrong %s about %s#%s method.",
                    key, method.getMethod().getDeclaringClass().getTypeName(), method.getName()));
        } else {
            throw new NotParametersConfigException(String.format(
                    "%s#%s method need to configure the @Parameters annotation.",
                    method.getMethod().getDeclaringClass().getTypeName(), method.getName()));
        }
    }

    private Object[] getParameters(FrameworkMethod method) {
        Object[] params = null;
        Object[][] parametersList = getParametersList(method);
        if (parametersList != null) {
            Integer index = getParamsIndexMap.get(method.getMethod());
            if (index == null) {
                index = 0;
            }
            if (index < parametersList.length) {
                params = parametersList[index];
                getParamsIndexMap.put(method.getMethod(), ++index);
            }
        }
        return params;
    }

    private class ParameterInvokeMethod extends Statement {
        private final FrameworkMethod fTestMethod;
        private Object fTarget;
        private Object[] args;

        public ParameterInvokeMethod(FrameworkMethod testMethod, Object target, Object[] params) {
            fTestMethod = testMethod;
            fTarget = target;
            args = params;
        }

        @Override
        public void evaluate() throws Throwable {
            fTestMethod.invokeExplosively(fTarget, args);
        }
    }

    public static class RunParametersProviderException extends RuntimeException {
        public RunParametersProviderException(String msg) {
            super(msg);
        }
    }

    public static class NotParametersConfigException extends RuntimeException {
        public NotParametersConfigException(String msg) {
            super(msg);
        }
    }

    public static class InvalidParametersConfigException extends RuntimeException {
        public InvalidParametersConfigException(String msg) {
            super(msg);
        }
    }
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JUnit 5 是 Java 编程语言的单元测试框架,它是 JUnit 团队开发的最新版本。JUnit 5 提供了一套强大的工具和功能,用于编写和执行单元测试。下面是 JUnit 5 单元测试的一些重要特性和用法: 1. 注解驱动:JUnit 5 使用注解来标记测试方法测试类。常用的注解包括 `@Test`、`@BeforeEach`、`@AfterEach` 等。 2. 断言方法JUnit 5 提供了丰富的断言方法,用于验证测试结果是否符合预期。例如,`assertEquals()`、`assertTrue()`、`assertNotNull()` 等。 3. 参数化测试JUnit 5 支持参数化测试,可以通过 `@ParameterizedTest` 注解来定义参数化测试方法,并使用 `@ValueSource`、`@CsvSource` 等注解提供测试参数。 4. 嵌套测试JUnit 5 允许在一个测试嵌套其他测试类,以更好地组织和管理测试代码。 5. 扩展模型:JUnit 5 引入了扩展模型,通过实现扩展接口可以自定义测试生命周期、测试报告、参数解析等行为。 6. 并发执行:JUnit 5 支持并发执行测试,可以通过 `@Execution` 注解来配置并发策略。 7. 动态测试JUnit 5 允许在运行动态生成测试用例,通过 `DynamicTest` 接口和 `@TestFactory` 注解实现。 8. 条件测试JUnit 5 提供了条件测试的功能,可以根据条件来决定是否执行某个测试方法。 以上是 JUnit 5 单元测试的一些重要特性和用法。如果你还有其他问题,请继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值