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

如何使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 22:13:16