【问题标题】:Mocking a class object using Mockito and PowerMockito使用 Mockito 和 PowerMockito 模拟类对象
【发布时间】:2012-08-29 00:00:57
【问题描述】:

是否可以使用 Mockito 和/或 PowerMockito 模拟类对象?

类似:

Class<Runnable> mockRunnableClass = mock(Class<Runnable>.class);

【问题讨论】:

  • 是的...这告诉我如何模拟最终类,但它没有告诉我如何模拟 Class 对象。
  • Java 泛型使用类型擦除,所以Class&lt;Runnable&gt;.class 不正确。 Classfinal,但无论如何你都可以使用 PowerMock。
  • 你想模拟“类”的用例是什么?上课很常见——只是好奇。
  • 如果你的测试发现了一个错误会发生什么?你不可以修吗?
  • 我没有声称文档是这样说的,它确实如此。我引用了它。 Mockito 并非旨在支持部分模拟。此外,我从未说过我不能更改代码来修复错误。修复错误和更改类的接口是两件非常不同的事情。第二个影响类的任何用户并迫使他们重写他们的代码。你不知道我在做什么的完整背景,所以当我说重构不是一种选择时,请不要忽视我。

标签: java junit mockito powermock


【解决方案1】:

模拟类的替代方法可能是使用工厂。我知道您担心重构,但这可以在不更改类的公共 API 的情况下完成。您没有提供太多代码来理解您尝试测试的类,但这里有一个在不更改 API 的情况下进行重构的示例。这是一个微不足道的课程,但它可能会给你一个想法。

public class Instantiator {

  public Runnable getNewInstance(Class<Runnable> runnableClass) throws Exception {
    return runnableClass.newInstance();
  }
}

当然,测试这个微不足道的类最简单的方法是使用真正的 Runnable 类,但如果您尝试模拟该类,就会遇到您遇到的问题。所以,你可以这样重构它:

public class PassThruFactory {
  public Object newInstance(Class<?> clazz) throws Exception {
    return clazz.newInstance();
  }
}

public class Instantiator {
  private PassThruFactory factory = new PassThruFactory();

  public Runnable getNewInstance(Class<Runnable> runnableClass) throws Exception {
    return (Runnable)factory.newInstance(runnableClass);
  }
}

现在,Instantiator 使用相同的公共 API 完成了它之前所做的(非常简单的)事情,并且不需要该类的任何客户端自己进行任何特殊的注入。但是,如果你想模拟工厂类并注入它,这很容易做到。

【讨论】:

  • 使用工厂的好电话。
  • 你将如何测试 PassThruFactory 类?您是否建议忽略测试 PassThruFactory 类?
  • 很难用这些琐碎的例子来谈论这个,但你可以在不模拟任何东西的情况下测试 PassThruFactory。您传入一个类并断言您获得了该类型的对象。
【解决方案2】:

如果您无法重构代码,为什么不使用代理,因为没有太多选择,正如@jherics 提到的,java 系统类由引导类加载器加载,而 powermock 无法重新定义它们的字节码。

然而,Powermock 现在带有一个代理,它将允许系统类模拟。查看here了解完整说明。

主要思想是修改你的java命令并添加:

-javaagent: path/to/powermock-module-javaagent-1.4.12.jar

该代理所做的基本工作是取消类的最终确定,以允许将来在特定测试中进行模拟,这就是为什么您需要使用特定类型与代理进行通信的原因,例如使用 JUnit:

@Rule PowerMockRule rule = new PowerMockRule(); // found in the junit4 rule agent jar

还支持TestNG。只需查看wiki page 了解更多信息。

希望对您有所帮助。

【讨论】:

  • 这看起来可行,但我们的引导过程非常复杂。不能修改 Java 代理。
【解决方案3】:

首先,如 cmets 所述,您需要执行以下操作:

Class<Runnable> mockRunnableaClass = (Class<Runnable>)mock(Class.class);

但由于 PowerMock 的限制,这不会以通常的方式工作。您不能简单地从 java.lang、java.net、java.io 或其他系统类中模拟类,因为它们是由 Java 的引导类加载器加载的,并且不能由 PowerMock 的类加载器操作字节码。 (请参阅PowerMock FAQ #4。)从 PowerMock 1.2.5 开始,您可以解决此问题。如果您要测试的课程是这样的:

public class ClassToTest {
  private Class<Runnable> runnableClass;

  public void setRunnableClass(Class<Runnable> runnableClass) {
    this.runnableClass = runnableClass;
  }

  public Runnable foo() {
    return runnableClass.newInstance();
  }
}

然后你会这样做:

@RunWith(PowerMockRunner.class)
@PrepareForTest({ ClassToTest.class }) // Prepare the calling class for test
public class SystemClassUserTest {

  @Test
  public void testFoo() throws Exception {
    Class<Runnable> mockClass = (Class<Runnable>) mock(Class.class);
    Runnable mockRunnable = mock(Runnable.class);

    ClassToTest objectUT = new ClassToTest();
    objectUT.setRunnableClass(mockClass);

    when(mockClass.newInstance()).thenReturn(mockRunnable);
    assertThat(objectUT.foo(), is(sameInstance(mockRunnable);
  }
}

【讨论】:

  • 上面的不太行。当我调用PowerMockito.mock(Class.class) 时,测试运行程序会抛出IllegalAccessError 的一个实例。
  • 我实际上并没有对其进行测试,因此Class 可能过于基础而无法模拟。您使用的是哪个版本的 PowerMock?
  • 1.4.10 我相信。恐怕你是对的。这可能是无法干净地测试代码的情况之一。
【解决方案4】:

这个怎么样。在 PCService 类中创建一个对象 (MS) 的 get 方法,然后对其进行模拟。

public class PCService implements PCServiceIf {
    public MSIf getMS() {
        return ms;
    }

    private MSIf ms = new MS();
    public boolean isMovieAccessibleToMyLevel(String myLevel, String movieId)  {
        return  getMS().getPCL(movieId);
    }
}


@Test
public void testIsMovieAccessibleToMyLevelMock()  {
    msMock = mock(MS.class);
    spy  = spy(new PCService());

    doReturn(msMock).when(spy).getMS();
    when(msMock.getPCL(movieId)).thenReturn(value);
    when(spy.getMS().getPCL(movieId)).thenReturn(value);
    assertTrue(spy.isMovieAccessibleToMyLevel("PG", movieId) == true);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-23
    • 2018-09-05
    • 2014-09-21
    相关资源
    最近更新 更多