一尘不染

用Java模拟静态块

java

我对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上运行DependentClassTestClassWithStaticInitTest因为您实际上希望为它运行静态块ClassWithStaticInitTest

您将如何完成此任务?还是您认为任何更好的,非基于JMockit的解决方案更干净?


阅读 174

收藏
2020-12-03

共1个答案

一尘不染

遇到此问题时,我通常会执行与您描述的相同的操作,除了将静态方法设置为受保护的方法之外,以便可以手动调用它。最重要的是,我确保可以多次调用该方法而不会出现问题(否则就测试而言,它并不比静态初始化程序好)。

这工作得相当好,我可以实际测试静态初始化方法是否达到了我期望/想要的方式。有时,拥有一些静态初始化代码是最容易的,而构建一个过于复杂的系统来替换它是不值得的。

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

2020-12-03