在 Java 中模拟静态块

我对Java的座右铭是“仅仅因为Java有静态块,并不意味着你应该使用它们。撇开玩笑不谈,Java中有很多技巧使测试成为一场噩梦。我最讨厌的两个是匿名类和静态块。我们有很多使用静态块的旧代码,这些是我们编写单元测试的烦人点之一。我们的目标是能够以最少的代码更改为依赖于此静态初始化的类编写单元测试。

到目前为止,我对同事的建议是将静态块的主体移动到私有静态方法中并调用它。然后可以从静态块中调用此方法。对于单元测试,另一个依赖于这个类的类可以很容易地用JMockit来模拟不做任何事情。让我们在示例中看到这一点。staticInitstaticInit

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 上运行,因为您实际上希望静态块运行 。DependentClassTestClassWithStaticInitTestClassWithStaticInitTest

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


答案 1

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

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

PowerMock 不使用 Java 代理程序,因此不需要修改 JVM 启动参数。您只需添加jar文件和上述注释即可。


答案 2

有时,我会在我的代码所依赖的类中找到静态初始化器。如果我无法重构代码,我会使用 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是我的两位同事开发的开源项目。


推荐