嘲笑的静态块在Java
-
09-06-2019 - |
题
我的座右铭是"只是因为Java有静态块,这并不意味着你应该使用他们。" 开开玩笑,也有很多技巧在Java,使测试一个噩梦。两个最大的我讨厌都是匿名的课程以及静态块。我们有很多的遗产代码使用的静态块,这些都是一个恼人的点在我们推动编写单元的测试。我们的目标是能够编写的单元测试的课程,取决于这种静态的初始以最小的代码变化。
到目前为止,我建议,我的同事被动主体的静态阻止进入私人静态的方法,并呼吁它 staticInit
.这一方法随后可从静态块。对单元的测试,另一类是取决于此类可以很容易地嘲笑 staticInit
与JMockit不要做任何事情。让我们看看这个例子。
public class ClassWithStaticInit {
static {
System.out.println("static initializer.");
}
}
会被改变
public class ClassWithStaticInit {
static {
staticInit();
}
private static void staticInit() {
System.out.println("static initialized.");
}
}
因此,我们可以做以下在JUnit.
public class DependentClassTest {
public static class MockClassWithStaticInit {
public static void staticInit() {
}
}
@BeforeClass
public static void setUpBeforeClass() {
Mockit.redefineMethods(ClassWithStaticInit.class, MockClassWithStaticInit.class);
}
}
然而这种解决方案也都有其自己的问题。你不能运行 DependentClassTest
和 ClassWithStaticInitTest
在同一JVM因为你实际上想要的静态模块,运行为 ClassWithStaticInitTest
.
什么会是你的方式完成这任务吗?或者更好,非JMockit基础的解决方案,你认为会工作的吸尘器?
解决方案
当我遇到这个问题,我通常做同样的事情你描述,除了我的静态的方法保护的这样我就可以调用。在此,我要确保的方法可以调用的多次没有问题,(否则就是没有更好的比静初尽的测试).
这一工作相当良好,并且实际上我可以测试,在初的静态的方法并什么我期望/想要它做的。有时这只是最简单到有一些静态的初始化时代,它只是不值得建立一个过于复杂的系统来取代它。
当我利用这一机制,我一定要的文件,该保护法仅仅是暴露于测试目的,与希望它不会被用于通过其他开发商。这当然可能不是一个可行的解决方案,例如,如果类的接口是外部可见(无论是作为一个子组件的某种其他的团队,或者作为一个公共框架)。这是一个简单的解决方案的问题,虽然,并不需要一个第三方图书馆设置起来(我)。
其他提示
这是要得到更多的"高级"JMockit.事实证明,你可以重新定义静初始化块JMockit通过创建一个 public void $clinit()
法。因此,而不是作出这种改变
public class ClassWithStaticInit {
static {
staticInit();
}
private static void staticInit() {
System.out.println("static initialized.");
}
}
我们可以离开 ClassWithStaticInit
并做以下的 MockClassWithStaticInit
:
public static class MockClassWithStaticInit {
public void $clinit() {
}
}
这将在事实上允许我们不做任何改变现有课程。
偶尔,我找到静态initilizers类中,我的代码。如果我无法"重构"的代码,我用 PowerMock's @SuppressStaticInitializationFor
注解到抑制的静态器:
@RunWith(PowerMockRunner.class)
@SuppressStaticInitializationFor("com.example.ClassWithStaticInit")
public class ClassWithStaticInitTest {
ClassWithStaticInit tested;
@Before
public void setUp() {
tested = new ClassWithStaticInit();
}
@Test
public void testSuppressStaticInitializer() {
asserNotNull(tested);
}
// more tests...
}
解更多关于 抑制有害行为.
免责声明:PowerMock是一个开放源项目开发的两位同事的地雷。
我听起来就像你是治疗症状:可怜的设计与依赖静态的初始化。也许一些重构是真正的解决方案。听起来好像你已经做了一点点重构与你的 staticInit()
功能,但或许这一职能需要调离的构造,而不是从一个静态的初始化程序。如果你可以不用静态的,初始期间,你将会更好。只有你可以做出这一决定(我看不到你的代码)但一些重构肯定会有帮助。
作为嘲讽,我使用EasyMock,但我遇到了同样的问题。副作用的初始化时的静态传统代码使测试的困难。我们的回答是"重构"的静态初始化程序。
你可以写你的测试的代码常规和易模型的静态的方法使用元编程。
Math.metaClass.'static'.max = { int a, int b ->
a + b
}
Math.max 1, 2
如果你不能用常规,你会真的需要重构的代码(也许可以注射的东西就像一个initializator).
亲切的问候
我想你真的想要某些种类的工厂,而不是静态的初始化程序。
一些组合的一个单独和一个抽象的工厂能够获得你的同样的功能的今天,并有良好的可测性,但这将增加一个相当大量的锅炉用盘编码,所以它可能是更好只是试图重构静的东西完全消失,或者如果你可以至少得到一些不太复杂的解决方案。
很难说,如果其可能没有看到你的代码。
我不是超级知识渊博,在模拟框架,所以,请纠正我,如果我是错误的但你不能有两种不同的模拟对象复盖的情况,你说?如
public static class MockClassWithEmptyStaticInit {
public static void staticInit() {
}
}
和
public static class MockClassWithStaticInit {
public static void staticInit() {
System.out.println("static initialized.");
}
}
然后你可以用它们在不同的情况下测试
@BeforeClass
public static void setUpBeforeClass() {
Mockit.redefineMethods(ClassWithStaticInit.class,
MockClassWithEmptyStaticInit.class);
}
和
@BeforeClass
public static void setUpBeforeClass() {
Mockit.redefineMethods(ClassWithStaticInit.class,
MockClassWithStaticInit.class);
}
分别。
不是一个真正的答案,但只是想知道-没有以任何方式"反向"的呼叫 Mockit.redefineMethods
?
如果没有这样的明确方法是存在的,不应该执行它再次在下面的时装做的伎俩?
Mockit.redefineMethods(ClassWithStaticInit.class, ClassWithStaticInit.class);
如果这样一种方法是存在的,你可以执行它在类的 @AfterClass
方法,并测试 ClassWithStaticInitTest
与"原始"静态的初始化框,因为如果没有任何改变,从同一JVM。
这只是一种预感,所以我可能会丢失的东西。