【问题标题】:How organize and test this code?如何组织和测试这段代码?
【发布时间】:2017-02-22 15:33:02
【问题描述】:

我对如何组织和测试如下代码存在概念上的疑问,其中对辅助方法的调用是类的所有公共方法的第一条指令。我的想法是让代码干净且可测试。

代码是一个示例,试图通过“缓存”类来说明这一点。这个类有一个可选的前缀,如果设置了,它将应用于缓存中的所有键。

import java.util.HashMap;


public class Cache {
    private HashMap<String, Integer> inMemoryCache;
    private String prefix;


    public Cache() {
        this.inMemoryCache = new HashMap<String, Integer>();
        prefix = null;
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    public int getValue(String key) throws NullPointerException {
        String prefixedKey = applyPrefixOrDefault(key);
        return inMemoryCache.get(prefixedKey);
    }

    public void setValue(String key, int value) {
        String prefixedKey = applyPrefixOrDefault(key);
        inMemoryCache.put(prefixedKey, value);
    }

    public boolean isCached(String key) {
        String prefixedKey = applyPrefixOrDefault(key);
        return inMemoryCache.containsKey(prefixedKey);
    }

    private String applyPrefixOrDefault(String key) {
        if (prefix == null) {
            return key;
        } else {
            return prefix + key;
        }
    }


    public static void main (String[] arg) {
        Cache cache = new Cache();
        cache.setPrefix("global:");
        cache.setValue("id", 4);
        int value = cache.getValue("id");
        System.out.println(value);
    }
}

这段代码向我提出了两个问题:

  1. 如果我有很多方法访问内部哈希表,将缓存在一个类中的行为与前缀在另一个类中的行为分开是否正确?

  2. 什么是最干净的测试方法?如果不考虑前缀,测试 getValue、setValue 和 isCached 很简单。使用前缀我们需要测试两件事,缓存的正确内部行为,我们还需要测试所有方法在访问数据之前调用 applyPrefixOrDefault。

这是一个常见的用例,我确信必须有一些设计模式来组织它。有什么想法吗?

【问题讨论】:

    标签: testing design-patterns architecture software-design


    【解决方案1】:

    在我看来,我们在这里缺少的是一个让我们设置缓存状态的构造函数。所以我会添加如下:

    public Cache() {
        this(null, new HashMap<String, Integer>());
    }
    
    public Cache(String prefix, Map<String, Integer> cache) {
        this.prefix = prefix;
        this.inMemoryCache = cache;
    }
    

    使用这个新的构造函数,您应该能够为每个可能的缓存状态编写测试用例。我还将applyPrefixOrDefault 方法的可见性更改为受保护或打包,以便测试代码可以访问它。例如,为了测试 GetValue 方法,我会这样写:

    public class EmptyCacheTests {
    
        private final Map<String, Integer> memory;
        private final String prefix;
        private final Cache cache;
    
        public EmptyCacheTests() {
             this.memory = new HasMap<String, Integer>();
             this.prefix = "foo";
             this.cache = new Cache(prefix, memory);
        }
    
        public void testGetValue() {
             String key = this.cache.applyPrefixOrDefault("bar")
             this.memory.put(key, 50);
             result = this.cache.getValue("bar");
             assertEquals(50, result, "The value retrieved is wrong!");
        }
    }
    

    这里的重点是,它允许测试设置缓存的内部状态,这样我们就可以针对许多不同的状态进行测试。

    【讨论】:

    • 如果我有很多方法(例如 10-20 个方法),你会考虑将前缀的行为分离到一个单独的类中,还是复制所有的测试用例(有前缀和没有前缀)?
    • 是的,如果你想测试带和不带前缀,在一个单独的类中分离前缀计算可以让你按组合构建测试,而不是枚举所有可能的测试用例。
    • 想一想,prefix属性与缓存无关,而是与key的计算/转换有关。因此,我将创建一个 KeyBuilder 对象,该对象将封装此前缀,并公开类似于applyPrefixOrDefaulttransformKey 方法。然后可以在缓存的构造函数中注入一个 KeyBuilder 对象,以便测试可以定义它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-25
    • 1970-01-01
    • 2023-03-07
    • 1970-01-01
    • 1970-01-01
    • 2011-04-16
    相关资源
    最近更新 更多