JUnit - 自定义 Rule

一、自定义 Rule

1、Custom Rule
java 复制代码
public class CustomTestRule implements TestRule {

    @Override
    public Statement apply(Statement base, Description description) {
        return new Statement() {

            @Override
            public void evaluate() throws Throwable {
                // 测试执行前
                beforeTest(description);
                try {
                    // 测试执行
                    base.evaluate();
                    // 测试成功
                    onTestSuccess(description);
                } catch (Throwable t) {
                    // 测试失败
                    onTestFailure(description, t);
                    throw t;
                } finally {
                    // 无论测试成功失败
                    afterTest(description);
                }
            }
        };
    }

    private void beforeTest(Description description) {
        System.out.println("开始测试: " + description.getMethodName());
    }

    private void onTestSuccess(Description description) {
        System.out.println("测试通过: " + description.getMethodName());
    }

    private void onTestFailure(Description description, Throwable t) {
        System.out.println("测试失败: " + description.getMethodName() + " - " + t.getMessage());
    }

    private void afterTest(Description description) {
        System.out.println("测试结束: " + description.getMethodName());
    }
}
2、Test
java 复制代码
public class CustomTestRuleTest {

    @Rule
    public CustomTestRule customTestRule = new CustomTestRule();

    @Test
    public void test() {
        System.out.println("执行测试逻辑...");
    }
}
复制代码
# 输出结果

开始测试: test
执行测试逻辑...
测试通过: test
测试结束: test

二、实例实操(重试 Rule)

1、Custom Rule
java 复制代码
public class RetryRule implements TestRule {

    private final int maxRetries;

    public RetryRule(int maxRetries) {
        this.maxRetries = maxRetries;
    }

    @Override
    public Statement apply(Statement base, Description description) {
        return new Statement() {

            @Override
            public void evaluate() throws Throwable {
                for (int attempt = 1; attempt <= maxRetries; attempt++) {
                    try {
                        System.out.printf("第 %d / %d 次尝试执行测试: %s%n", attempt, maxRetries, description.getMethodName());
                        base.evaluate();
                        System.out.printf("第 %d 次执行测试成功%n", attempt);
                        return;
                    } catch (Throwable e) {
                        System.err.printf("第 %d 次执行测试失败: %s%n", attempt, e.getMessage());
                        if (attempt < maxRetries) {
                            waitBeforeRetry(attempt);
                            continue;
                        }
                        throw e; // 超过最大重试次数,抛出异常
                    }
                }
            }
        };
    }

    private void waitBeforeRetry(int attempt) throws InterruptedException {

        // 指数退避策略
        long waitTime = Math.min(1000 * (long) Math.pow(2, attempt - 1), 10000);
        System.out.printf("等待 %dms 后重试...%n", waitTime);
        Thread.sleep(waitTime);
    }
}
2、Test
java 复制代码
public class RetryRuleTest {

    @Rule
    public RetryRule retryRule = new RetryRule(3);

    @Test
    public void test() {
        System.out.println("执行测试逻辑...");
    }
}
复制代码
# 输出结果

第 1 / 3 次尝试执行测试: test
执行测试逻辑...
第 1 次执行测试成功
java 复制代码
public class RetryRuleTest {

    @Rule
    public RetryRule retryRule = new RetryRule(3);

    @Test
    public void test() {
        int a = 10 / 0;
    }
}
复制代码
# 输出结果

第 1 / 3 次尝试执行测试: test
第 1 次执行测试失败: / by zero
等待 1000ms 后重试...
第 2 / 3 次尝试执行测试: test
第 2 次执行测试失败: / by zero
等待 2000ms 后重试...
第 3 / 3 次尝试执行测试: test
第 3 次执行测试失败: / by zero

三、关于异常重新抛出

java 复制代码
public class CustomTestRule implements TestRule {

    @Override
    public Statement apply(Statement base, Description description) {
        return new Statement() {

            @Override
            public void evaluate() throws Throwable {
                beforeTest(description);
                try {
                    base.evaluate();
                    onTestSuccess(description);
                } catch (Throwable t) {
                    onTestFailure(description, t);

                    // 异常重新抛出
                    throw t;
                } finally {
                    afterTest(description);
                }
            }
        };
    }

    private void beforeTest(Description description) {
        System.out.println("开始测试: " + description.getMethodName());
    }

    private void onTestSuccess(Description description) {
        System.out.println("测试通过: " + description.getMethodName());
    }

    private void onTestFailure(Description description, Throwable t) {
        System.out.println("测试失败: " + description.getMethodName() + " - " + t.getMessage());
    }

    private void afterTest(Description description) {
        System.out.println("测试结束: " + description.getMethodName());
    }
}
  1. 异常重新抛出确保了失败信息能传递到 JUnit 框架,如果不抛出,即使测试方法失败了,也会显示为成功状态

  2. TestRule 只是增强测试,而不是替代测试框架的错误处理

  3. 异常重新抛出保持测试的原始语义,只是在执行前后添加了额外的逻辑

相关推荐
小陈工2 小时前
Python Web开发入门(十七):Vue.js与Python后端集成——让前后端真正“握手言和“
开发语言·前端·javascript·数据库·vue.js·人工智能·python
H Journey2 小时前
C++之 CMake、CMakeLists.txt、Makefile
开发语言·c++·makefile·cmake
一定要AK6 小时前
Spring 入门核心笔记
java·笔记·spring
A__tao6 小时前
Elasticsearch Mapping 一键生成 Java 实体类(支持嵌套 + 自动过滤注释)
java·python·elasticsearch
SharpCJ6 小时前
Android 开发者为什么必须掌握 AI 能力?端侧视角下的技术变革
android·ai·aigc
KevinCyao6 小时前
java视频短信接口怎么调用?SpringBoot集成视频短信及回调处理Demo
java·spring boot·音视频
科技小花6 小时前
数据治理平台架构演进观察:AI原生设计如何重构企业数据管理范式
数据库·重构·架构·数据治理·ai-native·ai原生
lly2024066 小时前
C 标准库 - `<stdio.h>`
开发语言
一江寒逸6 小时前
零基础从入门到精通MySQL(中篇):进阶篇——吃透多表查询、事务核心与高级特性,搞定复杂业务SQL
数据库·sql·mysql
沫璃染墨6 小时前
C++ string 从入门到精通:构造、迭代器、容量接口全解析
c语言·开发语言·c++