You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用JUnit和Mockito测试Java中带日期参数的文件读取方法

测试接收日期并读取文件的方法(JUnit + Mockito 实战)

一、先明确要覆盖的测试场景

这个readFile方法的核心逻辑是日期转换→获取文件路径→读取文件→过滤提取符合长度的内容,需要覆盖以下核心场景:

  • 日期转换正常,文件存在且内容符合规则,能正确提取有效数据
  • 文件内容里存在不符合长度的条目,能过滤掉并记录错误日志
  • 获取到的文件路径为空,直接返回空列表
  • 文件不存在,捕获异常后返回空列表
  • 文件读取过程中发生IO错误,捕获异常后返回空列表

二、先改造原代码(提升可测试性)

原代码里直接new GetFiles()属于硬依赖,没法用Mockito mock,所以先把GetFiles作为依赖通过构造方法注入:

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FileReaderService {
    private static final Logger logger = LoggerFactory.getLogger(FileReaderService.class);
    private final GetFiles getFiles;

    // 构造注入依赖,方便测试时替换为mock对象
    public FileReaderService(GetFiles getFiles) {
        this.getFiles = getFiles;
    }

    public List<String> readFile(String date) {
        String dateConverted = FormatsDates.fileAEDateFormat(date);
        String readDataFile = getFiles.getFileAE(dateConverted);
        
        List<String> obtainList = new ArrayList<>();

        if (!readDataFile.isEmpty()) {
            try (FileReader fileReader = new FileReader(readDataFile); 
                 BufferedReader bufferedReader = new BufferedReader(fileReader)) {

                String separator;
                String line;
                int cont = 0;

                while ((line = bufferedReader.readLine()) != null) {
                    cont++;
                    if (cont > 1) {
                        line = line.replace(ConstantsFiles.LIN_WIT_SPA, ConstantsFiles.REP_SPA_LIN);
                        separator = line.substring(ConstantsFiles.POS_CLTE, ConstantsFiles.LONG_CLTE);
                        logger.info("Longitud correcta: " + separator);
                        
                        if (separator.length() == Integer.parseInt(ConstantsFiles.LENGTH_CLIENT)) {
                            obtainList.add(separator);
                        } else {
                            logger.error("Longitud incorrecta: " + separator);
                        }
                    }
                }
            } catch (FileNotFoundException ex) {
                logger.error("Error, archivo no encontrado: " + ex.getMessage(), ex);
            } catch (IOException ex) {
                logger.error("Error de lectura, archivo Ejecutivos Facultados: " + ex.getMessage(), ex);
            }
        }
        return obtainList;
    }
}

三、JUnit 5 + Mockito 测试代码示例

先确保项目引入了必要依赖(以Maven为例):

<!-- JUnit 5 核心依赖 -->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.9.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.9.2</version>
    <scope>test</scope>
</dependency>
<!-- Mockito 依赖 -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>4.11.0</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-junit-jupiter</artifactId>
    <version>4.11.0</version>
    <scope>test</scope>
</dependency>

测试类完整代码

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
public class FileReaderServiceTest {

    @Mock
    private GetFiles mockGetFiles; // 模拟依赖的GetFiles对象

    @InjectMocks
    private FileReaderService fileReaderService; // 自动将mock注入测试类

    // 测试正常提取有效数据的场景
    @Test
    void readFile_ValidFile_ReturnsValidEntries() throws IOException {
        // 1. 准备测试数据
        String testDate = "2024-05-20";
        String convertedDate = "20240520"; // 假设日期转换后的格式
        String validLine1 = "prefix" + "1234567890" + "suffix"; // 符合长度的separator
        String validLine2 = "pre" + "0987654321" + "suf";
        // 创建临时测试文件
        Path tempFile = Files.createTempFile("test_data", ".txt");
        Files.write(tempFile, List.of("表头行", validLine1, validLine2));

        // 2. 模拟依赖行为
        when(mockGetFiles.getFileAE(convertedDate)).thenReturn(tempFile.toString());
        // 如果需要mock静态方法FormatsDates.fileAEDateFormat,需引入mockito-inline依赖,写法如下:
        // try (MockedStatic<FormatsDates> mockedStatic = mockStatic(FormatsDates.class)) {
        //     mockedStatic.when(() -> FormatsDates.fileAEDateFormat(testDate)).thenReturn(convertedDate);
        //     // 执行测试逻辑
        // }

        // 3. 执行目标方法
        List<String> result = fileReaderService.readFile(testDate);

        // 4. 验证结果
        assertEquals(2, result.size());
        assertTrue(result.contains("1234567890"));
        assertTrue(result.contains("0987654321"));
        verify(mockGetFiles, times(1)).getFileAE(convertedDate); // 验证mock方法被调用次数

        // 清理临时文件
        Files.deleteIfExists(tempFile);
    }

    // 测试过滤不符合长度的条目
    @Test
    void readFile_FileHasInvalidEntries_FiltersThemOut() throws IOException {
        String testDate = "2024-05-20";
        String convertedDate = "20240520";
        String validLine = "prefix" + "1234567890" + "suffix";
        String invalidLine = "pre" + "123" + "suf"; // 长度不符合要求的separator
        Path tempFile = Files.createTempFile("test_data", ".txt");
        Files.write(tempFile, List.of("表头行", validLine, invalidLine));

        when(mockGetFiles.getFileAE(convertedDate)).thenReturn(tempFile.toString());

        List<String> result = fileReaderService.readFile(testDate);

        assertEquals(1, result.size());
        assertTrue(result.contains("1234567890"));

        Files.deleteIfExists(tempFile);
    }

    // 测试文件路径为空的情况
    @Test
    void readFile_EmptyFilePath_ReturnsEmptyList() {
        String testDate = "2024-05-20";
        when(mockGetFiles.getFileAE(anyString())).thenReturn("");

        List<String> result = fileReaderService.readFile(testDate);

        assertTrue(result.isEmpty());
        verify(mockGetFiles, times(1)).getFileAE(anyString());
    }

    // 测试文件不存在的场景
    @Test
    void readFile_FileNotFound_ReturnsEmptyList() {
        String testDate = "2024-05-20";
        String fakeFilePath = "/fake/dir/nonexist.txt";
        when(mockGetFiles.getFileAE(anyString())).thenReturn(fakeFilePath);

        List<String> result = fileReaderService.readFile(testDate);

        assertTrue(result.isEmpty());
    }
}

四、关键测试技巧说明

  • Mock外部依赖:用Mockito模拟GetFiles,避免真实调用文件路径获取逻辑,专注测试当前方法的核心逻辑
  • 静态方法处理:如果FormatsDates.fileAEDateFormat是静态方法,需引入mockito-inline依赖,通过MockedStatic模拟静态方法的返回值
  • 临时文件模拟:用Java NIO的Files.createTempFile创建真实临时文件,模拟真实读取场景,测试完成后及时清理
  • 日志验证:若需验证日志输出,可引入slf4j-test工具捕获日志,验证info/error日志是否符合预期

内容的提问来源于stack exchange,提问作者Jonathan Ortiz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 10:55:05