关于java单元测试(@Test)详解

好,这个是 Java 后端必会核心技能,而且面试 + 实战都会考
我按「是什么 → 怎么用 → 高级用法 → 常见坑 → 面试点」一步一步给你讲清楚 👌


Java 单元测试 @Test 详解(JUnit 版)

目标:
让你不仅会写 @Test,还知道为什么这么写


一、什么是 Java 单元测试?

单元测试(Unit Test) =
👉 对 最小可测试单元(方法) 进行验证
👉 不依赖数据库、不依赖网络、不依赖外部系统

特点:

  • 快(毫秒级)
  • 自动化
  • 可重复
  • 用来 防回归

二、@Test 是什么?

@TestJUnit 提供的注解,标记一个方法是“测试方法”。

JUnit 主要版本:

  • JUnit 4(老项目多)
  • JUnit 5(Jupiter)(新项目推荐)

三、JUnit 5 最基本用法(推荐)

1️⃣ 引入依赖(Maven)

<dependency>
  <groupId>org.junit.jupiter</groupId>
  <artifactId>junit-jupiter</artifactId>
  <version>5.10.0</version>
  <scope>test</scope>
</dependency>

2️⃣ 编写一个最简单的测试

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class MathServiceTest {

    @Test
    void testAdd() {
        int result = 2 + 3;
        assertEquals(5, result);
    }
}

📌 规则:

  • @Test 方法 不能有参数
  • 一般 public 可省(JUnit 5 支持)
  • 不返回值(void

四、断言(Assertions)才是灵魂

常用断言一览

assertEquals(expected, actual);
assertNotEquals(a, b);

assertTrue(condition);
assertFalse(condition);

assertNull(obj);
assertNotNull(obj);

assertSame(a, b);       // 引用相同
assertNotSame(a, b);

示例

@Test
void testUserName() {
    User user = new User("Tom");
    assertEquals("Tom", user.getName());
}

五、异常测试(必考)

✅ 正确写法(JUnit 5)

@Test
void testDivideByZero() {
    assertThrows(ArithmeticException.class, () -> {
        int x = 1 / 0;
    });
}

❌ 老项目常见(JUnit 4):

@Test(expected = ArithmeticException.class)

六、生命周期注解(非常重要)

JUnit 5 生命周期

@BeforeAll    // 类加载后,只执行一次(static)
@AfterAll     // 类结束前,只执行一次(static)

@BeforeEach   // 每个 @Test 前
@AfterEach    // 每个 @Test 后

示例

@BeforeEach
void init() {
    System.out.println("初始化");
}

@AfterEach
void cleanup() {
    System.out.println("清理");
}

七、测试命名规范(面试加分)

推荐格式

methodName_condition_expectedResult

例:

add_twoNumbers_returnSum()
login_wrongPassword_throwException()

八、@Test + Spring Boot(真实项目)

使用 @SpringBootTest

@SpringBootTest
class UserServiceTest {

    @Autowired
    private UserService userService;

    @Test
    void testFindUser() {
        User user = userService.findById(1L);
        assertNotNull(user);
    }
}

⚠️ 注意:

  • @SpringBootTest 启动整个容器
  • 比普通单元测试 慢很多
  • 不算“纯单元测试”,更像 集成测试

九、常见扩展注解(高频)

1️⃣ 禁用测试

@Disabled("暂时不用")
@Test
void testXXX() {}

2️⃣ 重复测试

@RepeatedTest(5)
void testRepeat() {}

3️⃣ 超时测试

@Test
void testTimeout() {
    assertTimeout(Duration.ofSeconds(1), () -> {
        Thread.sleep(500);
    });
}

十、参数化测试(进阶 & 面试亮点)

@ParameterizedTest
@ValueSource(ints = {1, 2, 3})
void testIsPositive(int number) {
    assertTrue(number > 0);
}

十一、JUnit 4 vs JUnit 5 对比(必会)

对比项JUnit 4JUnit 5
@Testorg.junit.Testorg.junit.jupiter.api.Test
生命周期@Before / @After@BeforeEach / @AfterEach
异常expectedassertThrows
扩展RunnerExtension
推荐度

十二、单元测试的“红线”(血泪经验)

❌ 测试里写 System.out.println
❌ 测试依赖真实数据库
❌ 测试顺序有依赖
❌ 一个测试测 10 个逻辑
❌ 没有断言

✅ 一个测试只验证 一个行为
✅ 失败信息清晰
✅ 可重复执行


十三、面试高频问题(直接背)

Q:@Test 是做什么的?

标记方法为测试方法,由 JUnit 运行并校验结果。

Q:单元测试和集成测试区别?

单元测试不依赖外部系统,集成测试依赖 Spring、DB、RPC。

Q:为什么要写单元测试?

防回归、提高代码质量、提升重构信心。


十四、推荐组合(真实项目)

  • JUnit 5
  • Mockito(Mock 依赖)
  • AssertJ(断言更优雅)
  • Jacoco(覆盖率)

如果你愿意,我可以继续帮你:

  • 🔥 Mockito + @Test 实战(Mock Service / Mapper)
  • 🔥 Spring Boot 单元测试最佳实践
  • 🔥 一套面试级单元测试模板

你现在是 在写项目,还是 准备 Java 后端面试

Leave a Reply

您的邮箱地址不会被公开。 必填项已用 * 标注

Related Post