当前位置:首页 > 技术 > 正文内容

基于JUnit与Mockito的Spring Boot微服务测试策略

访客 技术 2026年7月17日 1

在现代微服务架构中,确保系统稳定性和功能正确性离不开有效的测试机制。Spring Boot结合JUnit与Mockito,为开发者提供了强大的单元与集成测试能力。本文将深入讲解如何通过这两个工具构建高效、可靠的测试体系。

一、单元测试设计与实现

单元测试聚焦于最小可测试单元,强调隔离性与可重复性。使用JUnit 5提供的注解和断言功能,可以快速编写清晰的测试用例。

以一个简单的计算服务为例:

@Service
public class MathCalculator {

    public double divide(double a, double b) {
        if (b == 0) throw new IllegalArgumentException("Cannot divide by zero");
        return a / b;
    }
}

对应的单元测试遵循AAA模式(准备、执行、验证):

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

class MathCalculatorTest {

    private final MathCalculator calculator = new MathCalculator();

    @Test
    void shouldReturnDivisionResultWhenDivisorIsNotZero() {
        // Arrange
        double dividend = 10.0;
        double divisor = 2.0;

        // Act
        double result = calculator.divide(dividend, divisor);

        // Assert
        assertEquals(5.0, result, "Division should yield correct result");
    }

    @Test
    void shouldThrowExceptionWhenDivisorIsZero() {
        // Arrange
        double dividend = 10.0;
        double divisor = 0.0;

        // Act & Assert
        assertThrows(IllegalArgumentException.class, () -> calculator.divide(dividend, divisor));
    }
}

二、依赖模拟:Mockito的应用

当被测类依赖外部组件(如数据库访问层、远程接口)时,应使用Mockito创建模拟对象,避免真实调用带来的不确定性。

假设存在一个订单处理服务,其依赖支付服务:

@Service
public class OrderProcessor {

    private final PaymentGateway paymentGateway;

    public OrderProcessor(PaymentGateway paymentGateway) {
        this.paymentGateway = paymentGateway;
    }

    public boolean confirmOrder(Order order) {
        return paymentGateway.charge(order.getAmount());
    }
}

使用Mockito进行模拟测试:

import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.assertTrue;

class OrderProcessorTest {

    @Test
    void shouldConfirmOrderWhenPaymentSucceeds() {
        // Arrange
        PaymentGateway mockGateway = mock(PaymentGateway.class);
        OrderProcessor processor = new OrderProcessor(mockGateway);

        Order testOrder = new Order(99.99);

        when(mockGateway.charge(99.99)).thenReturn(true);

        // Act
        boolean result = processor.confirmOrder(testOrder);

        // Assert
        assertTrue(result);
        verify(mockGateway).charge(99.99);
        verifyNoMoreInteractions(mockGateway);
    }
}

三、集成测试:端到端验证服务链路

集成测试用于验证多个组件协同工作的正确性。Spring Boot提供@SpringBootTest注解启动完整的应用上下文,并可通过TestRestTemplate发起实际请求。

例如,测试一个用户信息接口:

@RestController
@RequestMapping("/api/v1/users")
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> findById(@PathVariable Long id) {
        User user = userService.findById(id);
        return user != null ? ResponseEntity.ok(user) : ResponseEntity.notFound().build();
    }
}

集成测试代码如下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.Test;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserControllerIntegrationTest {

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    void shouldReturnUserWhenValidIdProvided() {
        // Act
        ResponseEntity<User> response = restTemplate.getForEntity("/api/v1/users/1", User.class);

        // Assert
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(response.getBody()).isNotNull();
        assertThat(response.getBody().getId()).isEqualTo(1L);
    }

    @Test
    void shouldReturnNotFoundWhenInvalidIdProvided() {
        // Act
        ResponseEntity<User> response = restTemplate.getForEntity("/api/v1/users/999", User.class);

        // Assert
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
    }
}

四、测试实践建议

  • 保持测试独立:每个测试不应受其他测试影响,不依赖外部环境或共享状态。
  • 合理使用Mock:仅对非核心逻辑或外部依赖进行模拟,避免过度模拟导致测试失真。
  • 覆盖边界场景:包括异常输入、空值、超时等极端情况。
  • 自动化集成:将测试纳入CI流程,确保每次提交均触发完整测试套件。
标签: Spring Boot

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。