正如许多人所说,单身人士很糟糕,这是一种“反模式”。幸运与否,我使用了一些具有它的代码,它不会在不久的将来消失。所以我的问题如下。
具有州的单身人士:
import java.util.HashMap;
import java.util.Map;
public enum Singleton {
INSTANCE;
private final Map<String, String> _mapper = new HashMap<String, String>();
public String getValue(String key) {
return _mapper.get(key);
}
public void addEntry(String key, String val) {
_mapper.put(key, val);
}
public String removeKey(String key) {
return _mapper.remove(key);
}
}
非常愚蠢的测试,没有考虑在测试运行中保留单例INSTANCE状态:
import org.junit.Assert;
import org.junit.Test;
public class SingletonTest {
public static final String KEY_1 = "key_1";
@Test public void testOne() {
Singleton.INSTANCE.addEntry(KEY_1, "val_1");
Singleton.INSTANCE.addEntry("key_2", "val_2");
}
@Test public void testTwo() {
Assert.assertNull(Singleton.INSTANCE.getValue(KEY_1));
}
}
如何在测试运行之间清理状态?可以用JUnit Runners完成吗?
对象的实际状态更复杂,并通过构造函数初始化。我正在寻找类似的东西:http://powermock.googlecode.com/svn/docs/powermock-1.3.5/apidocs/org/powermock/core/classloader/annotations/PrepareForTest.html
答案 0 :(得分:1)
您可以使用在执行每个测试后执行的@After
修饰的方法。在此方法中,您可以清除枚举中的地图状态:
@After
public void clear() {
Singleton.INSTANCE.removeKey(KEY_1);
Singleton.INSTANCE.removeKey("key_2");
//and on and on...
}
当然,更好的方法是在clear
中使用enum
方法清除地图的值,这样@After
方法的主体就会缩短:
@After
public void clear() {
//assuming you can create such method
Singleton.INSTANCE.clearEntries();
}
答案 1 :(得分:0)
一种选择是在枚举上包含一个包私有clear
方法。这并不是完全万无一失的,但是这种方法有望变得足够简单,以至于它完全没有错误。在您的示例中,它只需调用_mapper.clear()
。
另一种选择是使用与Delegate
相同的方法创建一个类Singleton
,并让Singleton
只委托给该类的实例。然后,您测试Delegate
(不 Singleton
),每次都实例化一个新的。
public enum Singleton {
final Delegate delegate = new Delegate();
public String getValue(String key) {
return delegate.getValue(key);
}
// etc
}
class Delegate { // probably best as package-private
private final Map<String, String> _mapper = new HashMap<String, String>();
public String getValue(String key) {
return _mapper.get(key);
}
// etc
}
这为您提供了一个非全局状态的Delegate
类,更容易测试。 Singleton
类中的全局状态是它在枚举的类加载时创建的全局Delegate
实例。
当然,测试任何使用Singleton
的类都会比较棘手,但这似乎不是你的问题。
答案 2 :(得分:0)
这是你可能想要考虑的事情。
enum Singleton {
INSTANCE;
ThreadLocal<Map<String, String>> context = new ThreadLocal<Map<String, String>>();
private final Map<String, String> _mapper = new HashMap<String, String>();
void createContext() {
context.set(new HashMap<String, String>());
}
Map<String, String> getMapper() {
Map<String, String> mapper;
mapper = context.get();
if (mapper==null) {
mapper = _mapper;
}
return mapper;
}
public String getValue(String key) {
return getMapper().get(key);
}
// ...
}
在测试中,您可以在设置过程中致电INSTANCE.createContext()
。
如果从未调用createContext
,则不会创建线程局部变量,因此使用正常的_mapper
。否则,将检索并使用线程局部变量。