嘲笑一个单身人士课程
我最近读到,制作一个单独的课程使得不可能嘲笑课程的对象,这使得测试它的客户变得很困难。 我无法立即明白其根本原因。 有人可以解释一下,嘲笑一个单身人士课程是不可能的吗? 此外,还有更多的问题与创建一个单独的类相关吗?
当然,我可以写一些不使用单例的东西,他们是邪恶的,使用Guice / Spring /不管怎样,但是首先,这不会回答你的问题,其次,当你使用遗留代码的时候,你有时不得不处理单例。例。
所以,我们不要讨论singleton的好坏(还有另一个问题),但让我们看看在测试过程中如何处理它们。 首先,我们来看看单例的一个常见实现:
public class Singleton {
private Singleton() { }
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
public String getFoo() {
return "bar";
}
}
这里有两个测试问题:
构造函数是私有的,所以我们不能扩展它(并且我们无法控制测试中实例的创建,但是,这是单例的重点)。
getInstance
是静态的,所以很难在使用单例的代码中注入伪造对象而不是单例对象。
对于基于继承和多态的嘲讽框架来说,这两点显然都是很大的问题。 如果你有代码的控制权,一种选择是通过添加一个允许调整内部字段的setter来使你的单例更“可测试”,如学习停止担心和爱Singleton(你甚至不需要嘲讽在这种情况下框架)。 如果你不这样做,基于拦截和AOP概念的现代嘲讽框架可以克服前面提到的问题。
例如,Mocking Static Method Calls显示了如何使用JMockit Expectations来模拟单例。
另一种选择是使用PowerMock,它是Mockito或JMock的扩展,它允许模拟通常不像静态,最终,私有或构造函数方法那样可玩的东西。 你也可以访问一个类的内部。
嘲笑单身人士的最好方法是根本不使用它们,或者至少不是传统意义上的。 您可能需要查看的一些做法是:
所以,不要让你有一个像这样访问:
Singleton.getInstance().doSometing();
...将你的“单例”定义为一个接口,并有其他的东西来管理它的生命周期,并将它注入你需要的地方,例如作为一个私有实例变量:
@Inject private Singleton mySingleton;
然后当你单元测试依赖于单例的类/组件/等时,你可以很容易地注入它的模拟版本。
大多数依赖注入容器将允许您将组件标记为“单例”,但取决于容器来管理它。
使用上述做法可以更容易地对代码进行单元测试,并让您专注于功能逻辑而不是布线逻辑。 这也意味着你的代码真正开始成为真正的面向对象,因为任何静态方法(包括构造函数)的使用都是有争议的过程。 因此,您的组件也开始变得真正可重用。
查看Google Guice作为10的首发:
http://code.google.com/p/google-guice/
你也可以看看Spring和/或OSGi可以做这种事情。 那里有很多IOC / DI的东西。 :)
根据定义,一个单例只有一个实例。 因此它的创作受到班级本身的严格控制。 通常它是一个具体的类,而不是一个接口,并且由于它的私有构造函数,它不是可分类的。 此外,它的客户端(通过调用Singleton.getInstance()
或等价的)可以很容易地找到它,所以你不能轻易使用例如依赖注入来用模拟实例替换它的“真实”实例:
class Singleton {
private static final myInstance = new Singleton();
public static Singleton getInstance () { return myInstance; }
private Singleton() { ... }
// public methods
}
class Client {
public doSomething() {
Singleton singleton = Singleton.getInstance();
// use the singleton
}
}
对于模拟,你最好需要一个可以自由分类的接口,并且通过依赖注入为其客户提供具体的实现。
您可以放松Singleton实现,使其可以被测试
setInstance
方法以允许在单元测试中替换实例 例:
interface Singleton {
private static final myInstance;
public static Singleton getInstance() { return myInstance; }
public static void setInstance(Singleton newInstance) { myInstance = newInstance; }
// public method declarations
}
// Used in production
class RealSingleton implements Singleton {
// public methods
}
// Used in unit tests
class FakeSingleton implements Singleton {
// public methods
}
class ClientTest {
private Singleton testSingleton = new FakeSingleton();
@Test
public void test() {
Singleton.setSingleton(testSingleton);
client.doSomething();
// ...
}
}
正如你所看到的,你只能通过损害Singleton的“清洁度”来使你的使用Singleton的代码单元可测试。 最后,如果可以避免,最好不要使用它。
更新:这里是Michael Feathers对遗留代码有效工作的强制性参考。
链接地址: http://www.djcxy.com/p/13819.html