【发布时间】:2010-01-30 07:40:55
【问题描述】:
我有一个成功通过测试的独立单例。但是对于一组测试,这会失败,因为一旦定义了单例,它就不允许重置实例。
关于如何解决这个问题的任何想法?
【问题讨论】:
我有一个成功通过测试的独立单例。但是对于一组测试,这会失败,因为一旦定义了单例,它就不允许重置实例。
关于如何解决这个问题的任何想法?
【问题讨论】:
我假设你的单例类中有一个私有静态字段来存储初始化的实例。
如果您不想修改代码,您可以定义一个在每次测试后运行的拆卸方法,并在此方法中通过反射将此静态字段设置为 null,如 here 所示。
【讨论】:
不要使用单例。
具体来说,单例和全局变量之间的唯一区别是单例尝试强制执行单个实例(例如,通过将构造函数设为私有)。
相反,将构造函数公开并使用新实例编写测试。在您的实际程序中,使用getInstance() 获取规范的全局实例(或使用 IOC 容器)。
记住singletons are pathological liars。
如果您仍然对 Singleton 的想法感到满意,那么您可以添加一个公共(和静态)工厂方法来创建实例,而不是让构造函数公开,从而以一种不会被意外使用的方式创建实例,例如:
public static MyClass TEST_CreateInstance() {
return new MyClass();
}
【讨论】:
Spring 为这个特定用例提供了DirtiesContext 注解,您需要为每个测试用例创建新的单例 bean 实例。它基本上为每个应用了此注释的测试用例/测试类创建一个新的应用程序上下文。
【讨论】:
你可以添加一个方法来销毁单例,例如destroyMe();您在其中取消初始化所有内容并将单例的实例设置为 null。
public void destroyMe(){
this.instance = null;
//-- other stuff to turn it off.
}
不过我会留下同步问题;)
但是为什么每次测试都需要重新初始化单例呢?它不应该基于单例的概念而有所不同。
【讨论】:
我强烈建议摆脱单例作为设计模式,并使用单例作为范围(依赖注入)。这只会让你的问题消失。
但是假设你被困在单例的世界里,那么你有几个选择取决于你是在测试单例还是依赖项。
如果您正在测试依赖项,那么您可以使用PowerMock 和JMockIt 模拟单例。有关如何进行此操作的说明,请参阅我的 previous post 关于模拟 Runtime.getRuntime。
如果你在测试 Singleton 那么你需要放宽构造规则,或者给 Singleton 一个“Reset”方法。
【讨论】:
通常要小心单例,它们通常是邪恶的、糟糕的设计,并且往往代表大的令人讨厌的全局变量(这不利于维护)。
仍然要先进行测试,您可以这样做:
static setInstance(...){ //package visibility or in difficult cases you have to use public
instance = ...;
}
正如所说,这更像是一种解决方法。因此,首先要进行测试,然后再从单例模式中重构出来。
【讨论】:
单例实例需要通过测试本身传递给 SUT - 这样您就可以为每个测试创建单例(并销毁)。采用 IoC 和模拟框架(如 Mockito)将使这种方法变得微不足道。
【讨论】:
在这里聚会已经很晚了,但是对于任何正在寻找答案的人,以防您不想/无法修改代码。
@BeforeEach
public void setup() {
object = Singleton.getInstance();
}
@AfterEach
public void after() {
// cleaning the singleton instance
ReflectionTestUtils.setField(object , "internal_object_name", null);
}
你的 Singleton 类应该是这样的:
public final class Singleton {
private static Singleton internal_object_name;
private Singleton (){}
public static Singleton getInstance() {
if (object == null)
return new Singleton();
else
return internal_object_name;
}
【讨论】: