java 使用 Mockito 和 PowerMockito 模拟类对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12169183/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 07:53:04  来源:igfitidea点击:

Mocking a class object using Mockito and PowerMockito

javajunitmockitopowermock

提问by LandonSchropp

Is it possible to mock a class object using Mockito and/or PowerMockito?

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

Something like:

就像是:

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

采纳答案by jhericks

An alternative to mocking Class might be to use a Factory instead. I know you are concerned about refactoring, but this could be done without changing the public API of the class. You haven't provided much code to understand the class you are trying to test, but here's an example of refactoring without changing the API. It's a trivial class, but it might give you an idea.

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

public class Instantiator {

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

Of course, the easiest thing to do to test this trivial class would be to use a genuine Runnable class, but if you tried to mock the Class, you would run into the problems you're having. So, you could refactor it thus:

当然,测试这个微不足道的类最简单的方法是使用真正的 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);
  }
}

Now Instantiator does exactly the (trivially simple) thing it was doing before with the same public API and no need for any client of the class to do any special injecting of their own. However, if you wanted to mock the factory class and inject it, that's very easy to do.

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

回答by jhericks

First, as stated in the comments, you would need to do:

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

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

But that won't work in the usual way because of a limitation with PowerMock. You cannot simply mock classes in from java.lang, java.net, java.io or other system classes because they're loaded by Java's bootstrap classloader and cannot be byte-code manipulated by PowerMock's classloader. (See PowerMock FAQ#4.) As of PowerMock 1.2.5, you can work around this. If the class you wanted to test was this:

但是由于 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();
  }
}

Then you would do this:

然后你会这样做:

@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);
  }
}

回答by Brice

why not using an agent if you can't refactor the code there isn't many options, as @jherics mentionned, java system classes are loaded by the bootstrap classloader and powermock can't redefine their bytecode.

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

However Powermock now coms with an agent, that will allow system classes mock. Check herefor complete explanation.

但是 Powermock 现在带有一个代理,这将允许系统类模拟。请在这里为完整的解释。

The main idea is to modify your java command and add :

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

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

The basic thing this agent is doing is to definalize classes, to allow future mocking in a specific test, that's why you'll need to use specific types to communicate with the agent, for example with JUnit :

这个代理所做的基本事情是取消类的最终化,以允许未来在特定测试中进行模拟,这就是为什么您需要使用特定类型与代理进行通信,例如与 JUnit :

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

TestNG is also supported. Just check the wiki pagefor more information.

也支持 TestNG。只需查看维基页面以获取更多信息。

Hope that helps.

希望有帮助。

回答by Mike

How about this. creating a get method of the has a Object (MS) in class PCService and then mock it.

这个怎么样。在类 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);
}