浅解 JUnit 4 第十二篇:如何生成 @Before 注解的替代品?(上)

背景

在 浅解 JUnit 4 第十一篇:@Before 注解和 @After 注解如何发挥作用? 一文中,我们初步探讨了 @Before 注解 和@After 注解是如何发挥作用的 。那么我们能否自己实现类似 @Before 注解的功能呢?(@After 注解和 @Before 注解的功能是对称的,如果能实现类似 @Before 注解的功能,那么应该也能实现类似 @After 注解的功能)

这个问题涉及的内容比较多,本文只是上篇。

正文

一个具体的例子

我在本地创建了一个小项目来以便探讨本文的问题,这个项目的结构如下 ⬇️

text 复制代码
.
├── pom.xml
└── src
    ├── main
    │   └── java
    │       └── org
    │           └── example
    │               └── SimpleAdder.java
    └── test
        └── java
            └── org
                └── study
                    ├── annotations
                    │   └── MyBefore.java
                    ├── rules
                    │   └── MyBeforeRule.java
                    └── SimpleAdderTest.java

SimpleAdder.java

SimpleAdder.java 的内容如下 ⬇️ 它的功能虽然很简单,但是用它来探讨本文的问题,也够用了。

java 复制代码
package org.example;

public class SimpleAdder {

    public int add(int a, int b) {
        return a + b;
    }
}

MyBefore.java

MyBefore.java 的内容如下 ⬇️

java 复制代码
package org.study.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyBefore {
}

我们会把 @MyBefore 作为 @Before 的替代品,而它的内容可以照抄 @Before,注意需要修改 package。

MyBeforeRule.java

MyBeforeRule.java 的内容如下 ⬇️

java 复制代码
package org.study.rules;

import org.junit.rules.MethodRule;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;
import org.junit.runners.model.TestClass;
import org.study.annotations.MyBefore;

import java.util.List;

public class MyBeforeRule implements MethodRule {

    @Override
    public Statement apply(Statement base, FrameworkMethod method, Object target) {
        TestClass testClass = new TestClass(target.getClass());
        List<FrameworkMethod> myBeforeMethods = testClass.getAnnotatedMethods(MyBefore.class);
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                for (var myBeforeMethod : myBeforeMethods) {
                    myBeforeMethod.invokeExplosively(target);
                }
                base.evaluate();
            }
        };
    }
}

MyBeforeRule 用于处理 @MyBefore 注解

SimpleAdderTest.java

SimpleAdderTest.java 的内容如下

java 复制代码
package org.study;

import org.example.SimpleAdder;
import org.junit.*;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
import org.study.annotations.MyBefore;
import org.study.rules.MyBeforeRule;

import java.util.Random;

public class SimpleAdderTest {

    private int a;
    private int b;

    private final SimpleAdder adder = new SimpleAdder();

    private static final int BOUND = 10;

    @Rule
    public final MyBeforeRule myBeforeRule = new MyBeforeRule();

    @MyBefore
    public void prepare() {
        Random random = new Random();
        a = random.nextInt(BOUND);
        b = random.nextInt(BOUND);
        System.out.printf("Randomly generated a is: %s%n", a);
        System.out.printf("Randomly generated b is: %s%n", b);
    }

    @Test
    public void testAdd() {
        int expectedResult = a + b;
        Assert.assertEquals(expectedResult, adder.add(a, b));
        String description = String.format("%s = %s + %s", expectedResult, a, b);
        System.out.println(description);
    }

    public static void main(String[] args) {
        Result result = JUnitCore.runClasses(SimpleAdderTest.class);
        for (Failure failure : result.getFailures()) {
            System.out.println(failure);
        }
    }
}

pom.xml

pom.xml 的内容如下

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>junit-study</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>25</maven.compiler.source>
        <maven.compiler.target>25</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

项目中出现的各个类的类图如下 ⬇️

运行结果

我们运行 SimpleAdderTest 中的 main 方法,应该可以看到类似这样的结果 ⬇️

其中 红色框 的内容和 prepare() 方法有关,黄色框 的内容和 testAdd() 方法有关。由于 a/b 这两个数字是随机生成的,所以你自己运行时,看到的 a/b 可能是其他值。

这样看来,我们的确实现了类似 @Before 注解的功能。但是这背后发生了什么呢?我们到下一篇再进行探讨吧。

其他

用 PlantUML 画图,所用到的代码

画 "类图" 所用的代码

puml 复制代码
@startuml
'https://plantuml.com/class-diagram

title 类图

annotation org.study.annotations.MyBefore

class org.example.SimpleAdder {
    + int add(int a, int b)
}

interface org.junit.rules.MethodRule {
    + Statement apply(Statement base, FrameworkMethod method, Object target)
}

class org.study.rules.MyBeforeRule

org.junit.rules.MethodRule <|.. org.study.rules.MyBeforeRule

class org.study.rules.MyBeforeRule {
    + Statement apply(Statement base, FrameworkMethod method, Object target)
}

class org.study.SimpleAdderTest {
    - int a;
    - int b;
    - final SimpleAdder adder
    - {static} final int BOUND
    + final MyBeforeRule myBeforeRule
    + void prepare()
    + void testAdd()
    + {static} void main(String[] args)
}

@enduml
相关推荐
川石课堂软件测试2 天前
涨薪技术|0到1学会性能测试第9课-HTTP响应统计
数据库·功能测试·网络协议·http·kubernetes·单元测试·测试用例
我命由我123453 天前
SQL 检测脚本
java·数据库·sql·功能测试·单元测试·压力测试·可用性测试
神一样的老师6 天前
WS63 嵌入式 SSD1306 OLED 驱动单元测试实战
单元测试·log4j
CesareCheung6 天前
高级软件测试工程师面试锦集
面试·单元测试·压力测试
2501_915918417 天前
在 Xcode 中运行和调试单元测试的完整指南
ide·vscode·macos·ios·单元测试·个人开发·xcode
极小狐8 天前
MR 里测试摘要空白、覆盖率不显示?JUnit 与覆盖率配置 9 步排查
数据库·ci/cd·junit·单元测试·mr·devops·代码覆盖率
toooooop88 天前
FastAdmin(ThinkPHP 5.0)单元测试实践:从零到跑通
单元测试·fastadmin
silvamux_8 天前
大模型 API 单元测试怎么 mock 不误伤
单元测试·log4j·php
Helix2508 天前
Python单元测试实战:pytest框架从入门到企业级测试体系搭建指南
python·单元测试·实战·mock·pytest·测试覆盖率·fixture
Best-Wishes9 天前
科来网络分析系统(技术交流版)使用说明书
开发语言·junit·php