如何使用JUnit与Mockito测试读取CSV的Cache类BufferedReader逻辑
测试这个Cache类的实操步骤
原代码有几个硬伤导致不好测试:硬写死了文件名、用静态Map会让测试用例之间互相干扰、没提供访问Map的入口、异常只打印不抛出。先把代码重构一下,让它好测:
@Component public class Cache implements InitializingBean { // 把静态Map改成实例变量,避免测试用例之间的状态污染 private Map<String, String> map = new HashMap<>(); // 把文件路径改成可注入的,测试时能指定测试文件 private final String csvFilePath; // 用构造方法注入路径,Spring环境下会自动读配置,测试时直接传测试文件路径就行 public Cache(@Value("${cache.csv.path:file.csv}") String csvFilePath) { this.csvFilePath = csvFilePath; } @Override public void afterPropertiesSet() throws Exception { // 用try-with-resources自动关流,避免资源泄漏 try (BufferedReader reader = new BufferedReader(new FileReader(csvFilePath))) { String details; while ((details = reader.readLine()) != null) { String[] values = details.split(",", 2); // 加个判断,避免数组越界 if (values.length >= 2) { map.put(values[0].trim(), values[1].trim()); } } } // 去掉catch里的printStackTrace,让异常抛出去,方便测试时验证异常场景 } // 给测试用的getter,返回不可修改的Map,防止外部篡改 public Map<String, String> getMap() { return Collections.unmodifiableMap(map); } }
接下来写JUnit 5的测试用例,覆盖几种常见场景:正常读取、空文件、文件不存在、格式错误的行。
先加测试依赖(Maven为例)
在pom.xml里加这些:
<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> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.24.2</version> <scope>test</scope> </dependency>
测试类代码
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import java.io.FileWriter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; class CacheTest { private Cache cache; // 每次测试完清理临时文件 @AfterEach void tearDown() throws IOException { Path tempFile = Path.of("test-cache.csv"); if (Files.exists(tempFile)) { Files.delete(tempFile); } } // 测试正常读取CSV的情况 @Test void afterPropertiesSet_shouldLoadCorrectData() throws Exception { // 先写个测试用的CSV文件 try (FileWriter writer = new FileWriter("test-cache.csv")) { writer.write("key1,value1\n"); writer.write("key2,value with comma,inside\n"); writer.write("key3,value3\n"); } // 初始化Cache,指定测试文件路径 cache = new Cache("test-cache.csv"); cache.afterPropertiesSet(); // 验证Map里的数据对不对 assertThat(cache.getMap()) .hasSize(3) .containsEntry("key1", "value1") .containsEntry("key2", "value with comma,inside") .containsEntry("key3", "value3"); } // 测试空CSV文件 @Test void afterPropertiesSet_shouldHandleEmptyFile() throws Exception { // 创建空文件 Files.createFile(Path.of("test-cache.csv")); cache = new Cache("test-cache.csv"); cache.afterPropertiesSet(); assertThat(cache.getMap()).isEmpty(); } // 测试文件不存在的情况 @Test void afterPropertiesSet_shouldThrowExceptionWhenFileNotFound() { cache = new Cache("non-existent-file.csv"); // 断言抛出异常,且根因是文件找不到 Exception exception = assertThrows(Exception.class, cache::afterPropertiesSet); assertThat(exception.getCause()).isInstanceOf(java.io.FileNotFoundException.class); } // 测试有格式错误行的CSV(比如没有逗号的行) @Test void afterPropertiesSet_shouldSkipInvalidLines() throws Exception { try (FileWriter writer = new FileWriter("test-cache.csv")) { writer.write("validKey,validValue\n"); writer.write("invalidLineWithoutComma\n"); writer.write("anotherValidKey,anotherValue\n"); } cache = new Cache("test-cache.csv"); cache.afterPropertiesSet(); // 验证只有有效行被加载 assertThat(cache.getMap()) .hasSize(2) .containsEntry("validKey", "validValue") .containsEntry("anotherValidKey", "anotherValue"); } }
如果是Spring Boot项目的集成测试
要是想测Spring容器启动时Cache有没有正确加载,用Spring Boot Test:
import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.TestPropertySource; import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest // 测试时指定用测试CSV文件 @TestPropertySource(properties = "cache.csv.path=test-integration-cache.csv") class CacheIntegrationTest { @Autowired private Cache cache; @Test void cacheShouldBeLoadedOnStartup() { assertThat(cache.getMap()) .isNotEmpty() .containsEntry("testKey", "testValue"); } }
内容的提问来源于stack exchange,提问作者Vinutha Vinu
相关产品推荐
相关产品推荐

