有效的Java Item 3(使用私有构造函数或枚举类型强制执行singleton属性)注意到:
使类成为单例可能会使测试其客户端变得困难,因为除非它实现了作为其类型的接口,否则不可能将模拟实现替换为单例.
出于测试目的,为什么实例化单个单例实例并测试其API是不够的?这不是客户会消费的吗?引用似乎意味着测试单例将涉及"模拟实现",但为什么这是必要的?
我已经看到了各种"解释",或多或少地重述了上面的引用.有人可以进一步解释这一点,最好用代码示例吗?
mcl*_*sen 10
如果你的单身人士在数据库上执行操作或将数据写入文件怎么办?你不希望在单元测试中发生这种情况.您可能希望模拟对象以在内存中执行某些操作,以便您可以验证它们而不会产生永久的副作用.单元测试应该是自包含的,不应该创建与数据库的连接,也不应该对可能发生故障的外部系统执行其他操作,然后导致单元测试因不相关的原因而失败.
伪java的例子(我是C#dev):
public class MySingleton {
private static final MySingleton instance = new MySingleton();
private MySingleton() { }
public int doSomething() {
//create connection to database, write to a file, etc..
return something;
}
public static MySingleton getInstance() {
return instance;
}
}
public class OtherClass {
public int myMethod() {
//do some stuff
int result = MySingleton.getInstance().doSomething();
//do some other suff
return something;
}
}
Run Code Online (Sandbox Code Playgroud)
为了测试myMethod我们必须进行实际的数据库调用,文件操作等
@Test
public void testMyMethod() {
OtherClass obj = new OtherClass();
//if this fails it might be because of some external code called by
//MySingleton.doSomething(), not necessarily the logic inside MyMethod()
Asserts.assertEqual(1, obj.myMethod());
}
Run Code Online (Sandbox Code Playgroud)
如果MySingleton是这样的话:
public class MyNonSingleton implements ISomeInterface {
public MyNonSingleton() {}
@Override
public int doSomething() {
//create connection to database, write to a file, etc..
return something;
}
}
Run Code Online (Sandbox Code Playgroud)
然后你可以将它作为依赖注入到MyOtherClass中,如下所示:
public class OtherClass {
private ISomeInterface obj;
public OtherClass(ISomeInterface obj) {
this.obj = obj;
}
public int myMethod() {
//do some stuff
int result = obj.doSomething();
//do some other stuff
return something;
}
}
Run Code Online (Sandbox Code Playgroud)
然后你可以这样测试:
@Test
public void TestMyMethod() {
OtherClass obj = new OtherClass(new MockNonSingleton());
//now our mock object can fake the database, filesystem etc. calls to isolate the testing to just the logic in myMethod()
Asserts.assertEqual(1, obj.myMethod());
}
Run Code Online (Sandbox Code Playgroud)
我个人认为这个说法是完全错误的,因为它假设单例对于单元测试是不可替换(可模拟)的。相反。例如,在 Spring 的依赖注入中,单例实际上是 DI 组件的默认模型。单例和依赖注入并不相互排斥,上面的陈述试图暗示这一点。
我同意任何不能模拟的东西都会使应用程序更难以测试,但是没有理由假设单例比应用程序中的任何其他对象更难模拟。
可能的问题是,单例是一个全局实例,当它可能处于太多不同的状态时,单元测试可能会由于单例状态的变化而显示不可预测的结果。但对此有一些简单的解决方案 - 模拟你的单例并让你的模拟具有更少的状态。或者以这样的方式编写测试,在依赖于它的每个单元测试之前重新创建(或重新初始化)该单例。或者,最好的解决方案是测试您的应用程序的单例的所有可能状态。最终,如果现实需要多种状态,例如数据库连接(断开/连接/连接/错误/...),那么无论您是否使用单例,您都必须处理它。
| 归档时间: |
|
| 查看次数: |
2071 次 |
| 最近记录: |