当前位置:  开发笔记 > 编程语言 > 正文

用Java模拟静态块

如何解决《用Java模拟静态块》经验,为你挑选了5个好方法。

我对Java的座右铭是"仅仅因为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);
  }
}

然而,这种解决方案也有其自身的问题.您无法在同一个JVM上运行DependentClassTest,ClassWithStaticInitTest因为您实际上希望运行静态块ClassWithStaticInitTest.

你完成这项任务的方式是什么?或者你认为哪种更好,非基于JMockit的解决方案更干净?



1> Jan Kronquis..:

PowerMock是另一个扩展EasyMock和Mockito的模拟框架.使用PowerMock,您可以轻松地从类中删除不需要的行为,例如静态初始化程序.在您的示例中,您只需将以下注释添加到JUnit测试用例:

@RunWith(PowerMockRunner.class)
@SuppressStaticInitializationFor("some.package.ClassWithStaticInit")

PowerMock不使用Java代理,因此不需要修改JVM启动参数.您可以简单地添加jar文件和上面的注释.


这应该是公认的答案。拯救了我的一天。

2> Cem Catikkas..:

这将进入更多"高级"JMockit.事实证明,您可以通过创建public void $clinit()方法在JMockit中重新定义静态初始化块.所以,而不是做出这种改变

public class ClassWithStaticInit {
  static {
    staticInit();
  }

  private static void staticInit() {
    System.out.println("static initialized.");
  }
}

我们不妨ClassWithStaticInit在以下情况下离开并执行以下操作MockClassWithStaticInit:

public static class MockClassWithStaticInit {
  public void $clinit() {
  }
}

这实际上允许我们不对现有类进行任何更改.



3> matsev..:

偶尔,我会在我的代码所依赖的类中找到静态初始化器.如果我不能重构代码,我使用PowerMock的@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是由我的两位同事开发的开源项目.



4> Justin Stand..:

听起来像你正在治疗一个症状:设计不良,依赖于静态初始化.也许一些重构是真正的解决方案.听起来你已经对你的staticInit()函数进行了一些重构,但是可能需要从构造函数调用该函数,而不是从静态初始化器调用.如果你可以取消静态初始化期间,你会更好.只有你可以做出这个决定(我看不到你的代码库)但是一些重构肯定会有所帮助.

至于模拟,我使用EasyMock,但我遇到了同样的问题.遗留代码中静态初始化器的副作用使测试变得困难.我们的答案是重构静态初始化器.



5> Mike Stone..:

当我遇到这个问题时,我通常做你描述的同样的事情,除了我保护静态方法所以我可以手动调用它.最重要的是,我确保可以多次调用该方法而不会出现问题(否则,就测试而言,它并不比静态初始化器好).

这工作得相当好,我实际上可以测试静态初始化方法是否符合我的期望/希望它做什么.有时最简单的方法就是拥有一些静态初始化代码,而构建一个过于复杂的系统来替换它是不值得的.

当我使用这种机制时,我确保记录受保护的方法仅出于测试目的而暴露,并希望其他开发人员不会使用它.这当然可能不是一个可行的解决方案,例如,如果类的接口是外部可见的(作为其他团队的某种子组件,或作为公共框架).这是一个简单的问题解决方案,并且不需要第三方库来设置(我喜欢).

推荐阅读
小妖694_807
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有