0

0

Spring Boot抽象类及其实现类的单元测试实践

碧海醫心

碧海醫心

发布时间:2025-10-28 16:01:17

|

731人浏览过

|

来源于php中文网

原创

Spring Boot抽象类及其实现类的单元测试实践

本文探讨了在spring boot应用中如何对抽象类及其具体实现进行单元测试。核心策略是针对具体实现类编写测试用例,并利用mockito等工具模拟其依赖项,以验证抽象逻辑和具体实现方法的正确性,确保代码质量。

1. 抽象类单元测试的挑战与核心策略

在Spring Boot应用开发中,我们常常会利用抽象类来封装通用逻辑,并通过抽象方法定义子类必须实现的行为。例如,CsvService 抽象类定义了从CSV文件读取数据的通用流程,但具体的CSV文件名和数据处理逻辑则由其子类 AirportService 实现。

public abstract class CsvService {
    public List readFromCsv(Class type, CsvToBeanFilter filter) {
        // ... 省略了从资源读取并解析CSV的通用代码 ...
        // 最终会调用 getData(csvToBean) 方法处理解析后的数据
        return new ArrayList<>(); // 简化示例
    }
    protected abstract String getFileName();
    protected abstract List getData(CsvToBean csvToBean); // 假设此方法在 readFromCsv 内部被调用
}

@Service
public class AirportService extends CsvService {
    @Override
    protected String getFileName() {
        return "airport.csv";
    }

    @Override
    protected List getData(CsvToBean csvToBean) {
        List airports = new ArrayList<>();
        for (AirportBean bean : csvToBean) {
            // ... 省略了具体的业务处理逻辑 ...
            airports.add(bean);
        }
        return airports;
    }
}

直接对抽象类 CsvService 进行单元测试是不可能的,因为它不能被实例化,并且包含未实现的抽象方法。因此,核心策略是:通过测试其具体实现类(如 AirportService)来间接验证抽象类中的非抽象逻辑,并直接测试具体类中实现的抽象方法。

对于 AirportService,我们的测试目标是其实现的 getFileName() 方法和 getData() 方法。

2. 单元测试环境准备

在Spring Boot项目中,通常使用 JUnit 配合 Mockito 来进行单元测试。

  • JUnit: 提供测试框架和断言方法。
  • Mockito: 用于创建和管理模拟对象(mock objects),隔离被测试单元与外部依赖。

为了在测试类中使用 Mockito,可以使用 @ExtendWith(MockitoExtension.class) (JUnit 5) 或 MockitoAnnotations.openMocks(this) (JUnit 4 @Before 方法中)。

  • @InjectMocks: 用于自动注入被测试的对象,并将 @Mock 注解的依赖项注入到其中。
  • @Mock: 用于创建模拟对象。
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.Arrays;
import java.util.List;

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

// 假设 CsvBean 和 CsvToBean 已经导入
// import com.opencsv.bean.CsvToBean;
// import com.example.model.AirportBean; // 你的数据模型

@ExtendWith(MockitoExtension.class) // JUnit 5
public class AirportServiceTest {

    @InjectMocks
    private AirportService airportService; // 被测试的具体服务

    // 如果 AirportService 自身有其他依赖,则在这里使用 @Mock
    // 例如:@Mock private SomeRepository someRepository;

    // 对于 getData() 方法,我们需要模拟传入的 CsvToBean 对象
    // 但它作为方法参数,不需要在这里声明为 @Mock 字段

    // ... 后续测试方法 ...
}

3. 编写 AirportService 的单元测试

我们将分别测试 AirportService 中 getFileName() 和 getData() 方法。

3.1. 测试 getFileName() 方法

getFileName() 方法是一个简单的具体实现,它只返回一个字符串。测试它非常直接,只需调用该方法并断言其返回值是否符合预期。

STORYD
STORYD

帮你写出让领导满意的精美文稿

下载
    @Test
    void testGetFileName() {
        String fileName = airportService.getFileName();
        assertEquals("airport.csv", fileName, "getFileName() 应该返回 'airport.csv'");
    }

3.2. 测试 getData() 方法

getData() 方法接收一个 CsvToBean 对象,并对其进行迭代处理。为了测试这个方法,我们需要模拟 CsvToBean 对象的行为,使其在迭代时返回我们预设的 AirportBean 列表。

CsvToBean 实现了 Iterable 接口,这意味着我们可以模拟其 iterator() 方法来控制其遍历行为。

    @Test
    void testGetData() {
        // 1. 准备模拟数据:创建预期从 CSV 中解析出的 AirportBean 列表
        AirportBean mockAirport1 = new AirportBean();
        mockAirport1.setCode("LAX");
        mockAirport1.setName("Los Angeles International Airport");
        // 假设 AirportBean 还有其他字段,例如:
        // mockAirport1.setCity("Los Angeles");

        AirportBean mockAirport2 = new AirportBean();
        mockAirport2.setCode("JFK");
        mockAirport2.setName("John F. Kennedy International Airport");
        // mockAirport2.setCity("New York");

        List expectedAirportsFromCsv = Arrays.asList(mockAirport1, mockAirport2);

        // 2. 模拟 CsvToBean 对象及其行为
        // 创建一个 CsvToBean 的模拟实例
        CsvToBean mockCsvToBean = Mockito.mock(CsvToBean.class);

        // 当 mockCsvToBean 的 iterator() 方法被调用时,返回我们预设数据列表的迭代器
        when(mockCsvToBean.iterator()).thenReturn(expectedAirportsFromCsv.iterator());

        // 3. 调用被测试方法
        List actualAirports = airportService.getData(mockCsvToBean);

        // 4. 断言结果
        assertNotNull(actualAirports, "返回的机场列表不应为 null");
        assertEquals(2, actualAirports.size(), "返回的机场数量应为 2");

        // 验证返回的数据内容是否与预期相符
        assertEquals("LAX", actualAirports.get(0).getCode());
        assertEquals("Los Angeles International Airport", actualAirports.get(0).getName());

        assertEquals("JFK", actualAirports.get(1).getCode());
        assertEquals("John F. Kennedy International Airport", actualAirports.get(1).getName());

        // 验证 mockCsvToBean 的 iterator() 方法是否被调用了一次
        verify(mockCsvToBean, times(1)).iterator();
    }

3.3. 关于 readFromCsv() 方法的测试考量

CsvService 中的 readFromCsv() 方法包含了文件资源的加载 (new ClassPathResource(getFileName())) 和 CsvToBean 对象的创建逻辑。直接对这些内部创建的对象进行模拟(如 new ClassPathResource())在标准 Mockito 中是比较困难的,因为它不直接支持模拟构造函数调用。

如果需要对 readFromCsv() 的整个流程进行测试,通常有以下几种方式:

  1. 重构代码 将 Resource 或 InputStream 作为依赖注入到 CsvService 中,这样在测试时就可以轻松模拟这些依赖。这是提高可测试性的最佳实践。
  2. 集成测试: 准备一个真实的测试CSV文件放在 src/test/resources 目录下,然后让 readFromCsv() 实际读取这个文件。这更接近集成测试而非纯粹的单元测试。
  3. 高级模拟工具: 使用 PowerMockito 等工具可以模拟构造函数和静态方法,但它会增加测试的复杂性,并且通常不推荐过度使用。

在单元测试中,我们更倾向于隔离测试单元。因此,对于 AirportService,我们主要关注其具体实现的 getFileName() 和 getData() 方法,通过模拟 getData() 的输入来验证其逻辑。

4. 注意事项与总结

  • 优先测试具体实现类: 抽象类本身无法直接测试,应通过其具体子类来验证其定义的行为和抽象方法的实现。
  • 利用 Mockito 隔离依赖: 当具体类的方法依赖于其他服务、数据库访问或复杂对象(如 CsvToBean)时,使用 Mockito 创建模拟对象,控制它们的行为,从而将测试范围限制在当前被测试单元。
  • 关注单一职责: 每个测试方法应该只验证被测试单元的一个特定行为或功能。
  • 提高可测试性: 对于在方法内部创建的难以模拟的复杂对象(如 new ClassPathResource()),考虑重构代码,将其作为依赖注入,从而提高代码的可测试性和灵活性。
  • 区分单元测试与集成测试: 单元测试旨在验证最小代码单元的正确性,而涉及文件系统、网络或数据库等外部资源的测试更适合作为集成测试。

通过上述策略和示例,您应该能够在 Spring Boot 应用中有效地为抽象类及其具体实现编写健壮的单元测试。

相关专题

更多
spring框架介绍
spring框架介绍

本专题整合了spring框架相关内容,想了解更多详细内容,请阅读专题下面的文章。

98

2025.08.06

spring boot框架优点
spring boot框架优点

spring boot框架的优点有简化配置、快速开发、内嵌服务器、微服务支持、自动化测试和生态系统支持。本专题为大家提供spring boot相关的文章、下载、课程内容,供大家免费下载体验。

135

2023.09.05

spring框架有哪些
spring框架有哪些

spring框架有Spring Core、Spring MVC、Spring Data、Spring Security、Spring AOP和Spring Boot。详细介绍:1、Spring Core,通过将对象的创建和依赖关系的管理交给容器来实现,从而降低了组件之间的耦合度;2、Spring MVC,提供基于模型-视图-控制器的架构,用于开发灵活和可扩展的Web应用程序等。

384

2023.10.12

Java Spring Boot开发
Java Spring Boot开发

本专题围绕 Java 主流开发框架 Spring Boot 展开,系统讲解依赖注入、配置管理、数据访问、RESTful API、微服务架构与安全认证等核心知识,并通过电商平台、博客系统与企业管理系统等项目实战,帮助学员掌握使用 Spring Boot 快速开发高效、稳定的企业级应用。

64

2025.08.19

Java Spring Boot 4更新教程_Java Spring Boot 4有哪些新特性
Java Spring Boot 4更新教程_Java Spring Boot 4有哪些新特性

Spring Boot 是一个基于 Spring 框架的 Java 开发框架,它通过 约定优于配置的原则,大幅简化了 Spring 应用的初始搭建、配置和开发过程,让开发者可以快速构建独立的、生产级别的 Spring 应用,无需繁琐的样板配置,通常集成嵌入式服务器(如 Tomcat),提供“开箱即用”的体验,是构建微服务和 Web 应用的流行工具。

11

2025.12.22

Java Spring Boot 微服务实战
Java Spring Boot 微服务实战

本专题深入讲解 Java Spring Boot 在微服务架构中的应用,内容涵盖服务注册与发现、REST API开发、配置中心、负载均衡、熔断与限流、日志与监控。通过实际项目案例(如电商订单系统),帮助开发者掌握 从单体应用迁移到高可用微服务系统的完整流程与实战能力。

101

2025.12.24

软件测试常用工具
软件测试常用工具

软件测试常用工具有Selenium、JUnit、Appium、JMeter、LoadRunner、Postman、TestNG、LoadUI、SoapUI、Cucumber和Robot Framework等等。测试人员可以根据具体的测试需求和技术栈选择适合的工具,提高测试效率和准确性 。

428

2023.10.13

java测试工具有哪些
java测试工具有哪些

java测试工具有JUnit、TestNG、Mockito、Selenium、Apache JMeter和Cucumber。php还给大家带来了java有关的教程,欢迎大家前来学习阅读,希望对大家能有所帮助。

295

2023.10.23

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

7

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.1万人学习

C# 教程
C# 教程

共94课时 | 5.7万人学习

Java 教程
Java 教程

共578课时 | 40.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号