当前位置: 首页 > news >正文

朔州网站建设电商网站策划

朔州网站建设,电商网站策划,天美影视传媒的广告片拍摄技巧,做网站图片显示不来如何写好单元测试:Mock 脱离数据库,告别 SpringBootTest 的重型启动 作者:Killian(重庆) — 欢迎各位架构猎头、技术布道者联系我,项目实战丰富,代码稳健,Mock测试爱好者。 技术栈&a…

如何写好单元测试:Mock 脱离数据库,告别 @SpringBootTest 的重型启动

作者:Killian(重庆) — 欢迎各位架构猎头、技术布道者联系我,项目实战丰富,代码稳健,Mock测试爱好者。
技术栈:Java 17、JUnit 5、Mockito 5、Spring Boot 3.x(可选)


一、前言

你是否遇到过以下问题:

  • 每次跑测试都要加载整个 Spring 容器,慢如蜗牛?
  • 明明只测一个方法,却启动了 Redis、MySQL、MQ 等服务?
  • 想 Mock 一个 Bean 却被 @Autowired 绑死?

这时候,我们该说:不需要 @SpringBootTest!

本篇文章将系统讲解:

  • 如何编写真正的“单元”测试(Unit Test)
  • 如何使用 Mockito 精准 Mock 依赖,避免启动数据库等外部依赖
  • 如何写出高覆盖率、快反馈、可维护的业务逻辑测试

二、为什么要避免 @SpringBootTest?

问题描述
启动慢@SpringBootTest 会加载整个上下文(Controller、Service、Repository、Config)
依赖重需要配置数据库、缓存、RabbitMQ 等外部环境
不稳定环境不一致容易导致测试 flaky(有时通过,有时失败)
非单元测试实际上是“集成测试”,容易误用

三、正确的方式:使用 Mockito + JUnit 写真正的单元测试

示例背景

我们有一个服务类:

@Service
public class OrderService {private final OrderRepository orderRepository;private final PaymentClient paymentClient;public OrderService(OrderRepository orderRepository, PaymentClient paymentClient) {this.orderRepository = orderRepository;this.paymentClient = paymentClient;}public String pay(String orderId) {Order order = orderRepository.findById(orderId).orElseThrow(() -> new RuntimeException("订单不存在"));if (order.isPaid()) {return "重复支付";}boolean result = paymentClient.callPayGateway(order);if (result) {order.markPaid();orderRepository.save(order);return "支付成功";} else {return "支付失败";}}
}

单元测试写法(脱离容器 + Mock 依赖)

@ExtendWith(MockitoExtension.class)
class OrderServiceTest {@Mock OrderRepository orderRepository;@Mock PaymentClient paymentClient;@InjectMocks OrderService orderService;@Test@DisplayName("支付成功时,订单状态应更新并保存")void testPaySuccess() {Order mockOrder = new Order("123", false);when(orderRepository.findById("123")).thenReturn(Optional.of(mockOrder));when(paymentClient.callPayGateway(mockOrder)).thenReturn(true);String result = orderService.pay("123");assertEquals("支付成功", result);assertTrue(mockOrder.isPaid());verify(orderRepository).save(mockOrder);}@Test@DisplayName("找不到订单时,应抛出异常")void testOrderNotFound() {when(orderRepository.findById("999")).thenReturn(Optional.empty());assertThrows(RuntimeException.class, () -> orderService.pay("999"));}@Test@DisplayName("已支付订单不应重复支付,也不应保存")void testAlreadyPaid() {Order paidOrder = new Order("456", true);when(orderRepository.findById("456")).thenReturn(Optional.of(paidOrder));String result = orderService.pay("456");assertEquals("重复支付", result);verify(orderRepository, never()).save(any());}
}

四、关键技巧:Mock 什么?怎么 Mock?

1. 只 Mock “外部依赖”

  • 数据库 Repository
  • 第三方客户端(如 FeignClient、HttpClient)
  • Redis 操作、MQ 发送器、ES 操作器

2. 不 Mock 的部分

  • 自己写的业务逻辑类(即你要测的类)

3. 使用 Mockito 提供的能力

  • when(...).thenReturn(...):设置返回值
  • verify(...):验证方法是否调用
  • argThat(...):匹配参数条件
  • doThrow(...):模拟异常

五、单元测试 vs 集成测试:职责边界与框架选择

对比表格

维度单元测试(Unit Test)集成测试(Integration Test)
启动方式不启动 Spring 容器启动 Spring 容器(或部分)
测试目标业务逻辑、算法正确性Bean 交互、配置、环境集成
Mock 使用必须 Mock 外部依赖通常不 Mock,使用真实组件
性能快,毫秒级慢,秒级
数据源无数据库或 H2 Mock真正连接数据库(如 Docker 启动 MySQL)
断言粒度精确控制方法行为更偏向流程通路与集成稳定性

@DataJpaTest

用于测试 JPA Repository 层(不加载 Service、Controller):

@DataJpaTest
class UserRepositoryTest {@Autowired UserRepository repo;@Test@DisplayName("根据用户名查询用户,应返回结果")void testFindByUsername() {User u = new User("tom", "123");repo.save(u);assertTrue(repo.findByUsername("tom").isPresent());}
}

自动配置内嵌数据库(如 H2),速度适中,适合数据层测试。


@Mapper + MyBatis 的 Mapper 层测试(两种方式)

✅ 方式一:真实数据库 + @MybatisTest
@MybatisTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // 保持使用真实数据库配置
class OrderMapperTest {@Autowired OrderMapper orderMapper;@Test@DisplayName("根据订单ID查询,应返回订单信息")void testSelectById() {Order order = orderMapper.selectById("order123");assertNotNull(order);}
}

说明:

  • @MybatisTest 会只加载 MyBatis 相关的配置(不会加载 Service、Controller)
  • 默认使用 H2,可通过 @AutoConfigureTestDatabase 强制保留 MySQL 等真实库
  • 可以测试 XML 映射、注解 SQL、分页插件等
✅ 方式二:Mock Mapper(更适合单元测试)
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {@Mock OrderMapper orderMapper;@InjectMocks OrderService orderService;@Test@DisplayName("Mock Mapper 查询订单,应返回正确订单")void testOrderFetch() {Order mockOrder = new Order("order789", false);when(orderMapper.selectById("order789")).thenReturn(mockOrder);Order result = orderService.getOrder("order789");assertEquals("order789", result.getId());}
}

说明:

  • Mapper 在 Service 中作为依赖,Mock 掉即可测试业务逻辑
  • 不需要数据库、不用 @SpringBootTest,速度快、适合 CI

@WebMvcTest

用于测试 Controller 层(不加载业务逻辑):

@WebMvcTest(UserController.class)
class UserControllerTest {@Autowired MockMvc mockMvc;@MockBean UserService userService;@Test@DisplayName("调用 /hello 接口,应返回 hello tom")void testHelloApi() throws Exception {when(userService.getName()).thenReturn("tom");mockMvc.perform(get("/hello")).andExpect(status().isOk()).andExpect(content().string("hello tom"));}
}

优点是启动快,只加载 Web 层相关 Bean,可精准控制 Controller 输入输出。


要点说明
不使用 @SpringBootTest减少启动时间,提高测试速度
用 @ExtendWith(MockitoExtension.class)使用 Mockito 管理依赖注入
用 @InjectMocks注入被测类(业务类)
用 @Mock模拟依赖(Repository、外部接口)
每个测试只验证一件事保证测试原子性和可维护性

六、扩展阅读

  • Mockito 官方文档:https://site.mockito.org
  • JUnit 5 用户指南:https://junit.org/junit5/docs/current/user-guide/
  • 推荐阅读:Martin Fowler《Unit Test vs Integration Test》

七、结语

如果你写单元测试还依赖 @SpringBootTest,那就像每次微波炉加热都要重启电厂。Mock 依赖、聚焦业务、轻量高效,才是测试真正的姿势。

下一次写测试时,请问自己:“我是在测试业务逻辑,还是在启动一个服务器?”

本文由 @killian 原创,转载请注明出处。
☕ 请作者喝杯咖啡,持续更新更深入的干货

💡 彩蛋时间:如果你看到了这里,说明你是那种喜欢动手实战的人。那我悄悄分享一个开发圈流传的工具试用入口,貌似跟高效调试很有关系,地址也挺特别的:

🔗 入口

据说注册还能解锁一些隐藏功能,懂的都懂(别外传 😂)

http://www.hkea.cn/news/855212/

相关文章:

  • 做亚马逊网站费用吗曲靖新闻今日头条
  • bing 网站管理员2023今日新闻头条
  • 深圳市做网站前十强百度一下搜索网页
  • 做执法设备有哪些网站国家免费培训学校
  • 顺德乐从有做阿里巴巴的网站吗杭州网站设计
  • 做英文网站 用阿里服务器行吗b2b网站推广排名
  • 搭建网站做淘宝客网赌怎么推广拉客户
  • 网站建设前台与后台最新技术2021最新免费的推广引流软件
  • 做网站基本语言淘宝如何提升关键词排名
  • wordpress怎样分类目录添加标签seo文章范文
  • 订阅号可以做网站吗南宁seo外包服务商
  • 邢台哪儿做网站便宜宁波 seo排名公司
  • 深圳网站优化咨询网上广告怎么推广
  • 网站右击无效是怎么做的网络营销产品
  • 中宣部网站政治建设网站服务器是什么意思
  • 淮安网站定制徐州seo外包公司
  • 嘉兴类网站系统总部网站建设技术解决方案
  • 做网站的教科书外包网络推广公司推广网站
  • 模板名字 wordpress优化大师如何删掉多余的学生
  • 3d网站建设制作百度关键词优化手段
  • 新手做那些网站比较好东莞企业网站排名
  • 欧美风格网站360指数
  • 优秀网站建设公司电话下列哪些店铺适合交换友情链接
  • 58同城乌鲁木齐网站建设重庆网站到首页排名
  • wordpress知言主题山东服务好的seo公司
  • 旅游商务平台网站建设功能需求关键词排名查询官网
  • 做网站要搭建本地服务器么微商引流被加方法精准客源
  • 网站名字要备案吗友情链接怎么弄
  • 江苏网站开发外链网站大全
  • 网站代备案流程图百度关键词优化排名技巧