java - 如何模拟通过 Class.newInstance(className) 创建的对象?

标签 java unit-testing mockito

我正在尝试向一些遗留代码添加单元测试,这些遗留代码传递了一个 String 类名,并使用 Class.newInstance(String className) 创建了一个实现特定处理程序接口(interface)的对象。我可以控制我传递的类名,我可以获得指向新处理程序对象的指针(通过 getHandler() 调用),我想使用 Mockito 观察对它的调用。

我目前的解决方案是:

  1. 创建一个实现该接口(interface)的新测试类 TestHandler
  2. 让该测试类包含一个同样实现该接口(interface)的 Mockito 模拟对象。
  3. 手动将所有接口(interface)方法传递给模拟对象。
  4. 使模拟对象可通过 getMock() 方法访问。
  5. 通过对 objectUnderTest.getHandler().getMock() 进行 verify() 调用来观察对象。

这可行,但感觉有点不雅,尤其是必须手动编写所有传递方法。

有没有更好的解决方案?

最佳答案

从根本上说,您遇到的问题与尝试使用 new 测试新创建的实例时遇到的问题相同; Class.newInstance(可能是正确的Class.forName(foo).newInstance())不会伤害你,但也不会帮助你。

作为旁注,您的 TestHandler 听起来像是一个通用的委托(delegate)实现,无论如何它听起来都非常有用(特别是如果您需要编写 Handler 包装器)。如果是,您可能希望将其提升为与生产代码树中的处理程序相邻。

虽然我知道您提到了遗留代码,但如果允许您重构以包含测试接缝,这将变得非常容易。 (为便于解释,此处忽略反射异常。)

public ReturnType yourMethodUnderTest(String className) {
  return yourMethodUnderTest(Class.newInstance(className));
}

/** Package private for testing. */
public ReturnType yourMethodUnderTest(Handler handler) {
  return yourMethodUnderTest(Class.newInstance(className));
}

您还可以提取对象创建并在您的测试中替换它:

/** Instance field, package-private to replace in tests. */
Function<String, Handler> instanceCreator =
    ( x -> (Handler) Class.forName(x).newInstance());

public ReturnType yourMethodUnderTest(String className) {
  Handler handler = instanceCreator.apply(className);
  // ...
}

您甚至可以将它提取到一个方法中并在您的测试中替换它:

public ReturnType yourMethodUnderTest(String className) {
  Handler handler = createHandler(className);
  // ...
}

/** Package private for testing. */
Handler createHandler(String className) {
  return Class.forName(className).newInstance();
}

@Test public void yourTest() {
  // Manually replace createHandler. You could also use a Mockito spy here.
  ObjectUnderTest objectUnderTest = new ObjectUnderTest() {
    @Override Handler createHandler(String className) {
      return mock(Handler.class);
    }
  }
  // ...
}

旁注:即使 Mockito 创建了一个命名的动态类型,您几乎肯定无法破解它并允许您的代码通过名称创建它。这是因为对 mock registers the instance within Mockito's internal state 的调用.

// BAD: Unlikely to work
@Test public void yourTest() {
  objectUnderTest.methodUnderTest(
      mock(Handler.class).getClass().getName());
  // ...
}

关于java - 如何模拟通过 Class.newInstance(className) 创建的对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35366965/

相关文章:

Android Architecture Components GithubBrowser示例单元测试理解

java - 如何对将文件作为参数的方法进行单元测试和模拟

java - 嵌入式资源(媒体文件)在 jmf 和 Netbeans 中播放,但在制作项目的 jar 后视频无法播放

java - 焦点 JavaFX FXML 上的 KeyEvent

java - 在java中测试 volatile 变量

unit-testing - 单元或集成测试资源文件目录布局

unit-testing - Node.js 中的自动化 Web UI 测试

sql-server - 单元测试 SQL

java - 使用mockito测试服务

java - ResultActions 类型中的方法 andExpect(ResultMatcher) 不适用于参数 (RequestMatcher)