我问,因为我正在尝试使用一个不允许你模拟静态方法的模拟框架(Mockito)。调查一下我发现有不少博客文章说你应该尽可能少的静态方法,但是我很难理解为什么。特别是为什么不修改全局状态的方法基本上是辅助方法。例如,我有一个名为ApiCaller
的类,它有几个静态方法。静态方法的一个目的是执行HTTP调用,处理我们的服务器可能返回的任何自定义问题(例如用户未登录)并返回响应。为了简化,例如:
public class ApiCaller {
...
public static String makeHttpCall(Url url) {
// Performs logic to retrieve response and deal with custom server errors
...
return response;
}
}
要使用这一切,我所要做的就是致电ApiCaller.makeHttpCall(url)
现在,我可以轻松地将其设为非静态方法,如:
public class ApiCaller {
...
public String makeHttpCall(Url url) {
// Performs logic to retrieve response and deal with custom server errors
...
return response;
}
}
然后使用此方法调用new ApiCaller().makeHttpCall()
,但这似乎是额外的开销。任何人都可以解释为什么这是坏的,如果有一个更好的解决方案,使方法非静态(除了删除关键字),以便我可以使用模拟框架存根这些方法?
谢谢!
答案 0 :(得分:9)
静态方法的问题是,当它们与您尝试测试的系统无关时,它们很难伪造。想象一下这段代码:
public void systemUnderTest() {
Log.connectToDatabaseForAuditing();
doLogicYouWantToTest();
}
connectToDatabaseForAuditing()
方法是静态的。您不关心此方法对您要编写的测试的作用。但是,要测试此代码,您需要一个可用的数据库。
如果它不是静态的,代码将如下所示:
private Logger log; //instantiate in a setter AKA dependency injection/inversion of control
public void systemUnderTest() {
log.connectToDatabaseForAuditing();
doLogicYouWantToTest();
}
现在,如果没有数据库,您的测试将是微不足道的:
@Before
public void setUp() {
YourClass yourClass = new YourClass();
yourClass.setLog(new NoOpLogger());
}
//.. your tests
想象一下,当方法是静态的时,尝试这样做。除了修改记录器以使一个名为inTestMode
的静态变量在setUp()
中设置为true以确保它不连接到数据库之外,我无法想到一种方法。 / p>
答案 1 :(得分:2)
模块化程度较低。相反,您应该使用实例方法ApiCaller
定义接口makeHttpCall()
,以便将来可以定义单独的实现。
至少,您将始终拥有2个接口实现,原始版本和模拟版本。
(注意:有一些模拟框架允许你模拟静态方法)
作为附录,虽然在您的具体应用中可能并非如此,但通常使用静态方法表明设计监督较大。设计模块化和可重用性应该在整个应用程序中普遍存在,因为即使你现在不需要 你将来可能也需要它,而且它更难以实现事后改变事情要花费更多的时间。
答案 2 :(得分:1)
PRIVATE静态辅助方法并不坏,事实上,它们在我工作的大公司中实际上是首选。我一直使用Mockito,从调用静态辅助方法的方法访问。
编译器处理静态辅助方法的方式略有不同。创建的字节代码将生成invokestatic指令,如果删除静态,则结果将是其他指令之一,如invokespecial。 invokestatic加载类来访问方法的区别在于,invokespecial首先将对象弹出堆栈。因此可能会有轻微的性能优势(可能不是)。
答案 3 :(得分:0)
当你需要几乎回答你自己的问题时,你不能轻易地嘲笑它们。
特别是当它显示的内容时:进行HTTP调用非常昂贵,并且为 unit 测试代码执行此操作是没有意义的 - 除了集成测试之外。
单元测试需要来自HTTP调用的已知响应(和响应代码),如果您使用不受控制的网络呼叫某人 else的服务,则无法执行此操作。< / p>
答案 4 :(得分:0)
在我看来,这种解释非常简单明了,而且易于理解。
将实用程序方法声明为static
,通常会使您的代码更易于理解,这是一件好事。
但是,这种方法有一个严重的局限性:这类方法/类不容易被模拟。
因此,如果一个辅助方法具有任何外部依赖项(例如DB),这使得它(因此它的调用方)很难进行单元测试,则最好将其声明为非静态< / strong>。
这允许依赖项注入,从而使方法的调用者更易于进行单元测试。
来源:https://softwareengineering.stackexchange.com/a/111940/204271