如何用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
相关产品推荐
相关产品推荐

