如何测试实用程序项目的最终和静态方法?

我正在尝试为aproject实现unit testing,它使用遗留的“实用程序”项目,这个项目充满了静态方法,许多类都是最终的,或者它们的方法是最终的。 我根本无法更新遗留项目。

JMock和EasyMock都扼杀了最终方法,我没有看到一种测试静态调用的好方法。 有哪些技术可以测试这些?

如果您能够重构代码,则可以使用简单实例方法将调用包装到final / static方法中,例如:

protected Foo doBar(String name) { return Utility.doBar(name); } 

这允许您在unit testing中覆盖您的包装器方法以返回Foo的模拟实例。

或者你可以使用Powermock ,它扩展了Easymock(和Mockito),允许模拟最终和静态方法:

PowerMock是一个框架,可以扩展其他模拟库,例如EasyMock,具有更强大的function。 PowerMock使用自定义类加载器和字节码操作来实现静态方法,构造函数,最终类和方法的模拟,私有方法,静态初始化程序的删除等。

这是一个模拟静态final方法的示例测试,该示例演示了如何模拟其他类型:

 @Test public void testMockStaticFinal() throws Exception { mockStatic(StaticService.class); String expected = "Hello altered World"; expect(StaticService.sayFinal("hello")).andReturn("Hello altered World"); replay(StaticService.class); String actual = StaticService.sayFinal("hello"); verify(StaticService.class); assertEquals("Expected and actual did not match", expected, actual); // Singleton still be mocked by now. try { StaticService.sayFinal("world"); fail("Should throw AssertionError!"); } catch (AssertionError e) { assertEquals("\n Unexpected method call sayFinal(\"world\"):", e.getMessage()); } } 

如何进行间接/dependency injection?

由于遗留实用程序项目是您的依赖项,因此请创建一个接口以将其与代码分开。 现在,此接口的实际/生产实现委托给遗留实用程序方法。

 public LegacyActions : ILegacyActions { public void SomeMethod() { // delegates to final/static legacy utility method } } 

对于测试,您可以创建此接口的模拟,并避免与遗留实用程序交互。

JMockit允许您模拟静态方法和最终类。 我假设它使用了一些classloadin-fu,虽然我还没有真正研究过它。

JMockit Expectations API允许在任何类型的方法调用(在接口,抽象类,具体的最终或非最终类以及静态方法上)以及通过任何构造函数的类实例化上设置期望。

如果你的非重构方法使用类似JNDI的东西连接到另一个服务,我会考虑启动一个JDNI服务并用你控制的存根填充它。 这是一种痛苦,但相对简单。 它可能意味着设置一个数据库或JMS监听器或其他什么,但应该有一个轻量级的java实现,你可以放入测试。

正如已经指出的那样,可以使用JMockit 。 一个例子:

 @Test public void mockStaticAndFinalMethods(final LegacyService mock) { new NonStrictExpectations() { { LegacyService.staticMethod("hello"); result = "Hello altered World"; } }; String actual = LegacyService.staticMethod("hello"); new LegacyService().finalMethod(123, "test"); assertEquals("Hello altered World", actual); new Verifications() { { mock.finalMethod(123, "test"); // verify this call occurred at least once } }; } 

对于那些感兴趣的人,JMockit项目主页包含与PowerMock的比较。

如果需要,JMock和JDave可以模拟最终的方法和类。 这是说明。 话虽如此,我会将这些遗留代码(正如其他人已经建议的那样)视为外部依赖,并构建接口并模拟这些代码。 它是另一层间接,但由于你无法改变遗留代码,它似乎是一个合理的代码。